From 88285c79ccfeb92427b21e3d0ed0510f48fc7e09 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 4 Nov 2023 14:00:17 -0400 Subject: [PATCH 001/348] WIP hierarchical pathfinding --- core/src/mindustry/Vars.java | 2 + core/src/mindustry/ai/ControlPathfinder.java | 1 + .../src/mindustry/ai/HierarchyPathFinder.java | 223 ++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 core/src/mindustry/ai/HierarchyPathFinder.java diff --git a/core/src/mindustry/Vars.java b/core/src/mindustry/Vars.java index 0dc1cf9e3f..1dc36f8855 100644 --- a/core/src/mindustry/Vars.java +++ b/core/src/mindustry/Vars.java @@ -240,6 +240,7 @@ public class Vars implements Loadable{ public static WaveSpawner spawner; public static BlockIndexer indexer; public static Pathfinder pathfinder; + public static HierarchyPathFinder hpath; public static ControlPathfinder controlPath; public static FogControl fogControl; @@ -312,6 +313,7 @@ public class Vars implements Loadable{ spawner = new WaveSpawner(); indexer = new BlockIndexer(); pathfinder = new Pathfinder(); + hpath = new HierarchyPathFinder(); controlPath = new ControlPathfinder(); fogControl = new FogControl(); bases = new BaseRegistry(); diff --git a/core/src/mindustry/ai/ControlPathfinder.java b/core/src/mindustry/ai/ControlPathfinder.java index cabb297270..98033a4da2 100644 --- a/core/src/mindustry/ai/ControlPathfinder.java +++ b/core/src/mindustry/ai/ControlPathfinder.java @@ -17,6 +17,7 @@ import mindustry.world.*; import static mindustry.Vars.*; import static mindustry.ai.Pathfinder.*; +//TODO remove/replace public class ControlPathfinder{ //TODO this FPS-based update system could be flawed. private static final long maxUpdate = Time.millisToNanos(30); diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java new file mode 100644 index 0000000000..6a235cfb44 --- /dev/null +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -0,0 +1,223 @@ +package mindustry.ai; + +import arc.*; +import arc.graphics.*; +import arc.graphics.g2d.*; +import arc.math.*; +import arc.math.geom.*; +import arc.struct.*; +import mindustry.game.*; +import mindustry.game.EventType.*; +import mindustry.graphics.*; + +import static mindustry.Vars.*; +import static mindustry.ai.Pathfinder.*; + +public class HierarchyPathFinder{ + static final boolean debug = true; + + static final int[] offsets = { + 1, 0, //right: bottom to top + 0, 1, //top: left to right + 0, 0, //left: bottom to top + 0, 0 //bottom: left to right + }; + + static final int[] moveDirs = { + 0, 1, + 1, 0, + 0, 1, + 1, 0 + }; + + static final int[] nextOffsets = { + 1, 0, + 0, 1, + -1, 0, + 0, -1 + }; + + //maps pathCost -> flattened array of clusters in 2D + Cluster[][] clusters; + int clusterSize = 12; + + int cwidth, cheight; + + public HierarchyPathFinder(){ + + Events.on(WorldLoadEvent.class, event -> { + //TODO 5 path costs, arbitrary number + clusters = new Cluster[5][]; + clusterSize = 12; //TODO arbitrary + cwidth = Mathf.ceil((float)world.width() / clusterSize); + cheight = Mathf.ceil((float)world.height() / clusterSize); + + for(int cx = 0; cx < cwidth; cx++){ + for(int cy = 0; cy < cheight; cy++){ + createCluster(Team.sharded.id, costGround, cx, cy); + } + } + }); + + //TODO very inefficient, this is only for debugging + Events.on(TileChangeEvent.class, e -> { + createCluster(Team.sharded.id, costGround, e.tile.x / clusterSize, e.tile.y / clusterSize); + }); + + if(debug){ + Events.run(Trigger.draw, () -> { + int team = Team.sharded.id; + int cost = costGround; + + if(clusters == null || clusters[cost] == null) return; + + Draw.draw(Layer.overlayUI, () -> { + Lines.stroke(1f); + for(int cx = 0; cx < cwidth; cx++){ + for(int cy = 0; cy < cheight; cy++){ + var cluster = clusters[cost][cy * cwidth + cx]; + if(cluster != null){ + Draw.color(Color.green); + + Lines.rect(cx * clusterSize * tilesize - tilesize/2f, cy * clusterSize * tilesize - tilesize/2f, clusterSize * tilesize, clusterSize * tilesize); + Draw.color(Color.blue); + + for(int d = 0; d < 4; d++){ + IntSeq portals = cluster.portals[d]; + if(portals != null){ + int addX = moveDirs[d * 2], addY = moveDirs[d * 2 + 1]; + + for(int i = 0; i < portals.size; i++){ + int pos = portals.items[i]; + int from = Point2.x(pos), to = Point2.y(pos); + float width = tilesize * (Math.abs(from - to) + 1), height = tilesize; + + float average = (from + to) / 2f; + + float + x = (addX * average + cx * clusterSize + offsets[d * 2] * (clusterSize - 1) + nextOffsets[d * 2] / 2f) * tilesize, + y = (addY * average + cy * clusterSize + offsets[d * 2 + 1] * (clusterSize - 1) + nextOffsets[d * 2 + 1]/2f) * tilesize; + + Lines.ellipse(30, x, y, width / 2f, height / 2f, d * 90f - 90f); + } + } + } + } + } + } + Draw.reset(); + }); + }); + } + } + + void createCluster(int team, int pathCost, int cx, int cy){ + if(clusters[pathCost] == null) clusters[pathCost] = new Cluster[cwidth * cheight]; + Cluster cluster = clusters[pathCost][cy * cwidth + cx]; + if(cluster == null){ + cluster = clusters[pathCost][cy * cwidth + cx] = new Cluster(); + }else{ + //reset data + for(var p : cluster.portals){ + p.clear(); + } + cluster.innerEdges.clear(); + } + + //TODO look it up based on number. + PathCost cost = ControlPathfinder.costGround; + + for(int direction = 0; direction < 4; direction++){ + int otherX = cx + Geometry.d4x(direction), otherY = cy + Geometry.d4y(direction); + //out of bounds, no portals in this direction + if(otherX < 0 || otherY < 0 || otherX >= cwidth || otherY >= cheight){ + continue; + } + + Cluster other = clusters[pathCost][otherX + otherY * cwidth]; + IntSeq portals; + + if(other == null){ + //create new portals at direction + portals = cluster.portals[direction] = new IntSeq(); + }else{ + //share portals with the other cluster + portals = cluster.portals[direction] = other.portals[(direction + 2) % 4]; + } + + //Point2 adder = Geometry.d4[(direction + 1) % 4]; + + int addX = moveDirs[direction * 2], addY = moveDirs[direction * 2 + 1]; + int + baseX = cx * clusterSize + offsets[direction * 2] * (clusterSize - 1), + baseY = cy * clusterSize + offsets[direction * 2 + 1] * (clusterSize - 1), + nextBaseX = baseX + Geometry.d4[direction].x, + nextBaseY = baseY + Geometry.d4[direction].y; + + int lastPortal = -1; + boolean prevSolid = true; + + for(int i = 0; i < clusterSize; i++){ + int x = baseX + addX * i, y = baseY + addY * i; + + //scan for portals + if(solid(team, cost, x, y) || solid(team, cost, nextBaseX + addX * i, nextBaseY + addY * i)){ + int previous = i - 1; + //hit a wall, create portals between the two points + if(!prevSolid && previous >= lastPortal){ + //portals are an inclusive range + portals.add(Point2.pack(previous, lastPortal)); + } + prevSolid = true; + }else{ + //empty area encountered, mark the location of portal start + if(prevSolid){ + lastPortal = i; + } + prevSolid = false; + } + } + + //at the end of the loop, close any un-initialized portals; this is copy pasted code + int previous = clusterSize - 1; + if(!prevSolid && previous >= lastPortal){ + //portals are an inclusive range + portals.add(Point2.pack(previous, lastPortal)); + } + } + } + + Cluster cluster(int pathCost, int cx, int cy){ + return clusters[pathCost][cx + cwidth * cy]; + } + + private static boolean solid(int team, PathCost type, int x, int y){ + return x < 0 || y < 0 || x >= wwidth || y >= wheight || solid(team, type, x + y * wwidth, true); + } + + private static boolean solid(int team, PathCost type, int tilePos, boolean checkWall){ + int cost = cost(team, type, tilePos); + return cost == impassable || (checkWall && cost >= 6000); + } + + private static int cost(int team, PathCost cost, int tilePos){ + if(state.rules.limitMapArea && !Team.get(team).isAI()){ + int x = tilePos % wwidth, y = tilePos / wwidth; + if(x < state.rules.limitX || y < state.rules.limitY || x > state.rules.limitX + state.rules.limitWidth || y > state.rules.limitY + state.rules.limitHeight){ + return impassable; + } + } + return cost.getCost(team, pathfinder.tiles[tilePos]); + } + + static class Cluster{ + IntSeq[] portals = new IntSeq[4]; + IntSeq innerEdges = new IntSeq(); + + Cluster(){ + + } + + + } +} From 70538e29853439baa26caeac6eae04f6e789c22f Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 4 Nov 2023 17:18:53 -0400 Subject: [PATCH 002/348] suffering --- core/src/mindustry/Vars.java | 2 +- .../src/mindustry/ai/HierarchyPathFinder.java | 188 +++++++++++++++++- 2 files changed, 181 insertions(+), 9 deletions(-) diff --git a/core/src/mindustry/Vars.java b/core/src/mindustry/Vars.java index 1dc36f8855..755a9d63bb 100644 --- a/core/src/mindustry/Vars.java +++ b/core/src/mindustry/Vars.java @@ -313,8 +313,8 @@ public class Vars implements Loadable{ spawner = new WaveSpawner(); indexer = new BlockIndexer(); pathfinder = new Pathfinder(); - hpath = new HierarchyPathFinder(); controlPath = new ControlPathfinder(); + hpath = new HierarchyPathFinder(); fogControl = new FogControl(); bases = new BaseRegistry(); logicVars = new GlobalVars(); diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 6a235cfb44..dcc1d2f762 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -6,13 +6,17 @@ import arc.graphics.g2d.*; import arc.math.*; import arc.math.geom.*; import arc.struct.*; -import mindustry.game.*; +import arc.util.*; +import mindustry.content.*; import mindustry.game.EventType.*; +import mindustry.game.*; import mindustry.graphics.*; import static mindustry.Vars.*; import static mindustry.ai.Pathfinder.*; +//https://webdocs.cs.ualberta.ca/~mmueller/ps/hpastar.pdf +//https://www.gameaipro.com/GameAIPro/GameAIPro_Chapter23_Crowd_Pathfinding_and_Steering_Using_Flow_Field_Tiles.pdf public class HierarchyPathFinder{ static final boolean debug = true; @@ -80,7 +84,7 @@ public class HierarchyPathFinder{ Draw.color(Color.green); Lines.rect(cx * clusterSize * tilesize - tilesize/2f, cy * clusterSize * tilesize - tilesize/2f, clusterSize * tilesize, clusterSize * tilesize); - Draw.color(Color.blue); + Draw.color(Color.red); for(int d = 0; d < 4; d++){ IntSeq portals = cluster.portals[d]; @@ -102,6 +106,16 @@ public class HierarchyPathFinder{ } } } + + Draw.color(Color.magenta); + for(var con : cluster.cons){ + float + x1 = Point2.x(con.posFrom) * tilesize, y1 = Point2.y(con.posFrom) * tilesize, + x2 = Point2.x(con.posTo) * tilesize, y2 = Point2.y(con.posTo) * tilesize, + mx = (cx * clusterSize + clusterSize/2f) * tilesize, my = (cy * clusterSize + clusterSize/2f) * tilesize; + //Lines.curve(x1, y1, mx, my, mx, my, x2, y2, 20); + Lines.line(x1, y1, x2, y2); + } } } } @@ -124,6 +138,8 @@ public class HierarchyPathFinder{ cluster.innerEdges.clear(); } + //TODO: other cluster inner edges should be recomputed if changed. + //TODO look it up based on number. PathCost cost = ControlPathfinder.costGround; @@ -139,14 +155,12 @@ public class HierarchyPathFinder{ if(other == null){ //create new portals at direction - portals = cluster.portals[direction] = new IntSeq(); + portals = cluster.portals[direction] = new IntSeq(4); }else{ //share portals with the other cluster portals = cluster.portals[direction] = other.portals[(direction + 2) % 4]; } - //Point2 adder = Geometry.d4[(direction + 1) % 4]; - int addX = moveDirs[direction * 2], addY = moveDirs[direction * 2 + 1]; int baseX = cx * clusterSize + offsets[direction * 2] * (clusterSize - 1), @@ -185,6 +199,157 @@ public class HierarchyPathFinder{ portals.add(Point2.pack(previous, lastPortal)); } } + + connectInnerEdges(cx, cy, team, cost, cluster); + } + + static PathfindQueue frontier = new PathfindQueue(); + //node index -> total cost + static IntFloatMap costs = new IntFloatMap(); + + static IntSet usedEdges = new IntSet(); + + void connectInnerEdges(int cx, int cy, int team, PathCost cost, Cluster cluster){ + int minX = cx * clusterSize, minY = cy * clusterSize, maxX = Math.min(minX + clusterSize - 1, wwidth - 1), maxY = Math.min(minY + clusterSize - 1, wheight - 1); + + usedEdges.clear(); + cluster.cons.clear(); + + //TODO: how the hell to identify a vertex? + //cluster (i16) | direction (i2) | index (i14) + + for(int direction = 0; direction < 4; direction++){ + var portals = cluster.portals[direction]; + if(portals == null) continue; + + int addX = moveDirs[direction * 2], addY = moveDirs[direction * 2 + 1]; + + for(int i = 0; i < portals.size; i++){ + usedEdges.add(Point2.pack(direction, i)); + + int + portal = portals.items[i], + from = Point2.x(portal), to = Point2.y(portal), + average = (from + to) / 2, + x = (addX * average + cx * clusterSize + offsets[direction * 2] * (clusterSize - 1)), + y = (addY * average + cy * clusterSize + offsets[direction * 2 + 1] * (clusterSize - 1)); + + for(int otherDir = 0; otherDir < 4; otherDir++){ + var otherPortals = cluster.portals[otherDir]; + + for(int j = 0; j < otherPortals.size; j++){ + + //TODO redundant calculations? + if(!usedEdges.contains(Point2.pack(otherDir, j))){ + + int + other = otherPortals.items[j], + otherFrom = Point2.x(other), otherTo = Point2.y(other), + otherAverage = (otherFrom + otherTo) / 2, + ox = cx * clusterSize + offsets[otherDir * 2] * (clusterSize - 1), + oy = cy * clusterSize + offsets[otherDir * 2 + 1] * (clusterSize - 1), + otherX = (moveDirs[otherDir * 2] * otherAverage + ox), + otherY = (moveDirs[otherDir * 2 + 1] * otherAverage + oy); + + //HOW + if(Point2.pack(x, y) == Point2.pack(otherX, otherY)){ + if(true) continue; + + Log.infoList("self ", direction, " ", i, " | ", otherDir, " ", j); + System.exit(1); + } + + float connectionCost = astar( + team, cost, + minX, minY, maxX, maxY, + x + y * wwidth, + otherX + otherY * wwidth, + + (moveDirs[otherDir * 2] * otherFrom + ox), + (moveDirs[otherDir * 2 + 1] * otherFrom + oy), + (moveDirs[otherDir * 2] * otherTo + ox), + (moveDirs[otherDir * 2 + 1] * otherTo + oy) + + ); + + if(connectionCost != -1f){ + cluster.cons.add(new Con(Point2.pack(x, y), Point2.pack(otherX, otherY), connectionCost)); + + Fx.debugLine.at(x* tilesize, y * tilesize, 0f, Color.purple, + new Vec2[]{new Vec2(x, y).scl(tilesize), new Vec2(otherX, otherY).scl(tilesize)}); + } + } + } + } + } + } + } + + //distance heuristic: manhattan + private static float heuristic(int a, int b){ + int x = a % wwidth, x2 = b % wwidth, y = a / wwidth, y2 = b / wwidth; + return Math.abs(x - x2) + Math.abs(y - y2); + } + + private static int tcost(int team, PathCost cost, int tilePos){ + return cost.getCost(team, pathfinder.tiles[tilePos]); + } + + private static float tileCost(int team, PathCost type, int a, int b){ + //currently flat cost + return cost(team, type, b); + } + + /** @return -1 if no path was found */ + float astar(int team, PathCost cost, int minX, int minY, int maxX, int maxY, int startPos, int goalPos, int goalX1, int goalY1, int goalX2, int goalY2){ + frontier.clear(); + costs.clear(); + + costs.put(startPos, 0); + frontier.add(startPos, 0); + + if(debug && false){ + Fx.debugLine.at(Point2.x(startPos) * tilesize, Point2.y(startPos) * tilesize, 0f, Color.purple, + new Vec2[]{new Vec2(Point2.x(startPos), Point2.y(startPos)).scl(tilesize), new Vec2(Point2.x(goalPos), Point2.y(goalPos)).scl(tilesize)}); + } + + while(frontier.size > 0){ + int current = frontier.poll(); + + int cx = current % wwidth, cy = current / wwidth; + + //found the goal (it's in the portal rectangle) + //TODO portal rectangle approach does not work. + if((cx >= goalX1 && cy >= goalY1 && cx <= goalX2 && cy <= goalY2) || current == goalPos){ + return costs.get(current); + } + + for(Point2 point : Geometry.d4){ + int newx = cx + point.x, newy = cy + point.y; + int next = newx + wwidth * newy; + + if(newx > maxX || newy > maxY || newx < minX || newy < minY) continue; + + //TODO fallback mode for enemy walls or whatever + if(tcost(team, cost, next) == impassable) continue; + + float add = tileCost(team, cost, current, next); + float currentCost = costs.get(current); + + if(add < 0) continue; + + float newCost = currentCost + add; + + //a cost of 0 means "not set" + if(!costs.containsKey(next) || newCost < costs.get(next)){ + costs.put(next, newCost); + float priority = newCost + heuristic(next, goalPos); + frontier.add(next, priority); + } + } + } + + return -1f; } Cluster cluster(int pathCost, int cx, int cy){ @@ -213,11 +378,18 @@ public class HierarchyPathFinder{ static class Cluster{ IntSeq[] portals = new IntSeq[4]; IntSeq innerEdges = new IntSeq(); + Seq cons = new Seq<>(); + } - Cluster(){ + //TODO for debugging only + static class Con{ + int posFrom, posTo; + float cost; + public Con(int posFrom, int posTo, float cost){ + this.posFrom = posFrom; + this.posTo = posTo; + this.cost = cost; } - - } } From 2bcf5bf6843773811431f471d2ecf090620891f3 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 4 Nov 2023 17:37:29 -0400 Subject: [PATCH 003/348] Better connection storage --- .../src/mindustry/ai/HierarchyPathFinder.java | 110 ++++++++++++------ gradle.properties | 2 +- 2 files changed, 77 insertions(+), 35 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index dcc1d2f762..02eb26df09 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -7,9 +7,11 @@ import arc.math.*; import arc.math.geom.*; import arc.struct.*; import arc.util.*; +import mindustry.annotations.Annotations.*; import mindustry.content.*; import mindustry.game.EventType.*; import mindustry.game.*; +import mindustry.gen.*; import mindustry.graphics.*; import static mindustry.Vars.*; @@ -56,8 +58,8 @@ public class HierarchyPathFinder{ cwidth = Mathf.ceil((float)world.width() / clusterSize); cheight = Mathf.ceil((float)world.height() / clusterSize); - for(int cx = 0; cx < cwidth; cx++){ - for(int cy = 0; cy < cheight; cy++){ + for(int cy = 0; cy < cwidth; cy++){ + for(int cx = 0; cx < cheight; cx++){ createCluster(Team.sharded.id, costGround, cx, cy); } } @@ -84,29 +86,47 @@ public class HierarchyPathFinder{ Draw.color(Color.green); Lines.rect(cx * clusterSize * tilesize - tilesize/2f, cy * clusterSize * tilesize - tilesize/2f, clusterSize * tilesize, clusterSize * tilesize); - Draw.color(Color.red); + for(int d = 0; d < 4; d++){ IntSeq portals = cluster.portals[d]; if(portals != null){ - int addX = moveDirs[d * 2], addY = moveDirs[d * 2 + 1]; for(int i = 0; i < portals.size; i++){ int pos = portals.items[i]; int from = Point2.x(pos), to = Point2.y(pos); float width = tilesize * (Math.abs(from - to) + 1), height = tilesize; - float average = (from + to) / 2f; + portalToVec(cluster, cx, cy, d, i, Tmp.v1); - float - x = (addX * average + cx * clusterSize + offsets[d * 2] * (clusterSize - 1) + nextOffsets[d * 2] / 2f) * tilesize, - y = (addY * average + cy * clusterSize + offsets[d * 2 + 1] * (clusterSize - 1) + nextOffsets[d * 2 + 1]/2f) * tilesize; + Draw.color(Color.red); + Lines.ellipse(30, Tmp.v1.x, Tmp.v1.y, width / 2f, height / 2f, d * 90f - 90f); - Lines.ellipse(30, x, y, width / 2f, height / 2f, d * 90f - 90f); + LongSeq connections = cluster.portalConnections[d] == null ? null : cluster.portalConnections[d][i]; + + if(connections != null){ + Draw.color(Color.magenta); + for(int coni = 0; coni < connections.size; coni ++){ + long con = connections.items[coni]; + + portalToVec(cluster, cx, cy, IntraEdge.dir(con), IntraEdge.portal(con), Tmp.v2); + + float + x1 = Tmp.v1.x, y1 = Tmp.v1.y, + x2 = Tmp.v2.x, y2 = Tmp.v2.y, + mx = (cx * clusterSize + clusterSize / 2f) * tilesize, my = (cy * clusterSize + clusterSize / 2f) * tilesize; + //Lines.curve(x1, y1, mx, my, mx, my, x2, y2, 20); + Lines.line(x1, y1, x2, y2); + + } + } } } } + //TODO draw connections. + + /* Draw.color(Color.magenta); for(var con : cluster.cons){ float @@ -115,7 +135,7 @@ public class HierarchyPathFinder{ mx = (cx * clusterSize + clusterSize/2f) * tilesize, my = (cy * clusterSize + clusterSize/2f) * tilesize; //Lines.curve(x1, y1, mx, my, mx, my, x2, y2, 20); Lines.line(x1, y1, x2, y2); - } + }*/ } } } @@ -125,6 +145,20 @@ public class HierarchyPathFinder{ } } + void portalToVec(Cluster cluster, int cx, int cy, int d, int i, Vec2 out){ + int pos = cluster.portals[d].items[i]; + int from = Point2.x(pos), to = Point2.y(pos); + int addX = moveDirs[d * 2], addY = moveDirs[d * 2 + 1]; + float width = tilesize * (Math.abs(from - to) + 1), height = tilesize; + float average = (from + to) / 2f; + + float + x = (addX * average + cx * clusterSize + offsets[d * 2] * (clusterSize - 1) + nextOffsets[d * 2] / 2f) * tilesize, + y = (addY * average + cy * clusterSize + offsets[d * 2 + 1] * (clusterSize - 1) + nextOffsets[d * 2 + 1] / 2f) * tilesize; + + out.set(x, y); + } + void createCluster(int team, int pathCost, int cx, int cy){ if(clusters[pathCost] == null) clusters[pathCost] = new Cluster[cwidth * cheight]; Cluster cluster = clusters[pathCost][cy * cwidth + cx]; @@ -135,9 +169,11 @@ public class HierarchyPathFinder{ for(var p : cluster.portals){ p.clear(); } - cluster.innerEdges.clear(); } + //clear all connections, since portals changed, they need to be recomputed. + cluster.portalConnections = new LongSeq[4][]; + //TODO: other cluster inner edges should be recomputed if changed. //TODO look it up based on number. @@ -159,6 +195,9 @@ public class HierarchyPathFinder{ }else{ //share portals with the other cluster portals = cluster.portals[direction] = other.portals[(direction + 2) % 4]; + + //clear the portals, they're being recalculated now + portals.clear(); } int addX = moveDirs[direction * 2], addY = moveDirs[direction * 2 + 1]; @@ -206,18 +245,19 @@ public class HierarchyPathFinder{ static PathfindQueue frontier = new PathfindQueue(); //node index -> total cost static IntFloatMap costs = new IntFloatMap(); - + // static IntSet usedEdges = new IntSet(); void connectInnerEdges(int cx, int cy, int team, PathCost cost, Cluster cluster){ int minX = cx * clusterSize, minY = cy * clusterSize, maxX = Math.min(minX + clusterSize - 1, wwidth - 1), maxY = Math.min(minY + clusterSize - 1, wheight - 1); usedEdges.clear(); - cluster.cons.clear(); //TODO: how the hell to identify a vertex? //cluster (i16) | direction (i2) | index (i14) + //TODO: clear portal connections. also share them? + for(int direction = 0; direction < 4; direction++){ var portals = cluster.portals[direction]; if(portals == null) continue; @@ -236,6 +276,7 @@ public class HierarchyPathFinder{ for(int otherDir = 0; otherDir < 4; otherDir++){ var otherPortals = cluster.portals[otherDir]; + if(otherPortals == null) continue; for(int j = 0; j < otherPortals.size; j++){ @@ -251,15 +292,12 @@ public class HierarchyPathFinder{ otherX = (moveDirs[otherDir * 2] * otherAverage + ox), otherY = (moveDirs[otherDir * 2 + 1] * otherAverage + oy); - //HOW + //HOW (redundant nodes?) if(Point2.pack(x, y) == Point2.pack(otherX, otherY)){ - if(true) continue; - - Log.infoList("self ", direction, " ", i, " | ", otherDir, " ", j); - System.exit(1); + continue; } - float connectionCost = astar( + float connectionCost = innerAstar( team, cost, minX, minY, maxX, maxY, x + y * wwidth, @@ -273,10 +311,16 @@ public class HierarchyPathFinder{ ); if(connectionCost != -1f){ - cluster.cons.add(new Con(Point2.pack(x, y), Point2.pack(otherX, otherY), connectionCost)); + if(cluster.portalConnections[direction] == null) cluster.portalConnections[direction] = new LongSeq[cluster.portals[direction].size]; + if(cluster.portalConnections[otherDir] == null) cluster.portalConnections[otherDir] = new LongSeq[cluster.portals[otherDir].size]; + if(cluster.portalConnections[direction][i] == null) cluster.portalConnections[direction][i] = new LongSeq(8); + if(cluster.portalConnections[otherDir][j] == null) cluster.portalConnections[otherDir][j] = new LongSeq(8); - Fx.debugLine.at(x* tilesize, y * tilesize, 0f, Color.purple, - new Vec2[]{new Vec2(x, y).scl(tilesize), new Vec2(otherX, otherY).scl(tilesize)}); + //TODO: can there be duplicate edges?? + cluster.portalConnections[direction][i].add(IntraEdge.get(otherDir, j, connectionCost)); + cluster.portalConnections[otherDir][j].add(IntraEdge.get(direction, i, connectionCost)); + + //Fx.debugLine.at(x* tilesize, y * tilesize, 0f, Color.purple, new Vec2[]{new Vec2(x, y).scl(tilesize), new Vec2(otherX, otherY).scl(tilesize)}); } } } @@ -301,7 +345,7 @@ public class HierarchyPathFinder{ } /** @return -1 if no path was found */ - float astar(int team, PathCost cost, int minX, int minY, int maxX, int maxY, int startPos, int goalPos, int goalX1, int goalY1, int goalX2, int goalY2){ + float innerAstar(int team, PathCost cost, int minX, int minY, int maxX, int maxY, int startPos, int goalPos, int goalX1, int goalY1, int goalX2, int goalY2){ frontier.clear(); costs.clear(); @@ -377,19 +421,17 @@ public class HierarchyPathFinder{ static class Cluster{ IntSeq[] portals = new IntSeq[4]; - IntSeq innerEdges = new IntSeq(); - Seq cons = new Seq<>(); + //maps rotation + index of portal to list of IntraEdge objects + LongSeq[][] portalConnections = new LongSeq[4][]; } - //TODO for debugging only - static class Con{ - int posFrom, posTo; - float cost; + @Struct + static class IntraEdgeStruct{ + @StructField(8) + int dir; + @StructField(8) + int portal; - public Con(int posFrom, int posTo, float cost){ - this.posFrom = posFrom; - this.posTo = posTo; - this.cost = cost; - } + float cost; } } diff --git a/gradle.properties b/gradle.properties index a54f280cb0..f9fa0851e3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=1906938dea +archash=96f4f4214a From cfb0ea1c8cd7dc8f5d564a7a498291760a71eaf7 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 6 Nov 2023 19:21:29 -0500 Subject: [PATCH 004/348] Cluster A* progress --- .../src/mindustry/ai/HierarchyPathFinder.java | 171 ++++++++++++++++-- 1 file changed, 151 insertions(+), 20 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 02eb26df09..908bcb5de8 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -49,6 +49,17 @@ public class HierarchyPathFinder{ int cwidth, cheight; + static PathfindQueue frontier = new PathfindQueue(); + //node index -> total cost + static IntFloatMap costs = new IntFloatMap(); + // + static IntSet usedEdges = new IntSet(); + + static LongSeq tmpEdges = new LongSeq(); + + //node index (NodeIndex struct) -> node it came from + static IntIntMap cameFrom = new IntIntMap(); + public HierarchyPathFinder(){ Events.on(WorldLoadEvent.class, event -> { @@ -149,7 +160,6 @@ public class HierarchyPathFinder{ int pos = cluster.portals[d].items[i]; int from = Point2.x(pos), to = Point2.y(pos); int addX = moveDirs[d * 2], addY = moveDirs[d * 2 + 1]; - float width = tilesize * (Math.abs(from - to) + 1), height = tilesize; float average = (from + to) / 2f; float @@ -242,12 +252,6 @@ public class HierarchyPathFinder{ connectInnerEdges(cx, cy, team, cost, cluster); } - static PathfindQueue frontier = new PathfindQueue(); - //node index -> total cost - static IntFloatMap costs = new IntFloatMap(); - // - static IntSet usedEdges = new IntSet(); - void connectInnerEdges(int cx, int cy, int team, PathCost cost, Cluster cluster){ int minX = cx * clusterSize, minY = cy * clusterSize, maxX = Math.min(minX + clusterSize - 1, wwidth - 1), maxY = Math.min(minY + clusterSize - 1, wheight - 1); @@ -298,16 +302,14 @@ public class HierarchyPathFinder{ } float connectionCost = innerAstar( - team, cost, - minX, minY, maxX, maxY, - x + y * wwidth, - otherX + otherY * wwidth, - - (moveDirs[otherDir * 2] * otherFrom + ox), - (moveDirs[otherDir * 2 + 1] * otherFrom + oy), - (moveDirs[otherDir * 2] * otherTo + ox), - (moveDirs[otherDir * 2 + 1] * otherTo + oy) - + team, cost, + minX, minY, maxX, maxY, + x + y * wwidth, + otherX + otherY * wwidth, + (moveDirs[otherDir * 2] * otherFrom + ox), + (moveDirs[otherDir * 2 + 1] * otherFrom + oy), + (moveDirs[otherDir * 2] * otherTo + ox), + (moveDirs[otherDir * 2 + 1] * otherTo + oy) ); if(connectionCost != -1f){ @@ -378,17 +380,18 @@ public class HierarchyPathFinder{ if(tcost(team, cost, next) == impassable) continue; float add = tileCost(team, cost, current, next); - float currentCost = costs.get(current); if(add < 0) continue; - float newCost = currentCost + add; + float newCost = costs.get(current) + add; //a cost of 0 means "not set" - if(!costs.containsKey(next) || newCost < costs.get(next)){ + if(newCost < costs.get(next, Float.POSITIVE_INFINITY)){ costs.put(next, newCost); float priority = newCost + heuristic(next, goalPos); frontier.add(next, priority); + + //cameFrom.put(next, current); } } } @@ -396,6 +399,124 @@ public class HierarchyPathFinder{ return -1f; } + int makeNodeIndex(int cx, int cy, int dir, int portal){ + //to make sure there's only one way to refer to each node, the direction must be 0 or 1 (referring to portals on the top or right edge) + + //direction can only be 2 if cluster X is 0 (left edge of map) + if(dir == 2 && cx != 0){ + dir = 0; + cx --; + } + + //direction can only be 3 if cluster Y is 0 (bottom edge of map) + if(dir == 3 && cy != 0){ + dir = 1; + cy --; + } + return NodeIndex.get(cx + cy * cwidth, dir, portal); + } + + //distance heuristic: manhattan + private float clusterNodeHeuristic(int pathCost, int nodeA, int nodeB){ + int + clusterA = NodeIndex.cluster(nodeA), + dirA = NodeIndex.dir(nodeA), + portalA = NodeIndex.portal(nodeA), + clusterB = NodeIndex.cluster(nodeB), + dirB = NodeIndex.dir(nodeB), + portalB = NodeIndex.portal(nodeB); + + int rangeA = clusters[pathCost][clusterA].portals[dirA].items[portalA]; + int rangeB = clusters[pathCost][clusterB].portals[dirB].items[portalB]; + + float + averageA = (Point2.x(rangeA) + Point2.y(rangeA)) / 2f, + x1 = (moveDirs[dirA * 2] * averageA + (clusterA % cwidth) * clusterSize + offsets[dirA * 2] * (clusterSize - 1) + nextOffsets[dirA * 2] / 2f), + y1 = (moveDirs[dirA * 2 + 1] * averageA + (clusterA / cwidth) * clusterSize + offsets[dirA * 2 + 1] * (clusterSize - 1) + nextOffsets[dirA * 2 + 1] / 2f), + + averageB = (Point2.x(rangeB) + Point2.y(rangeB)) / 2f, + x2 = (moveDirs[dirB * 2] * averageB + (clusterB % cwidth) * clusterSize + offsets[dirB * 2] * (clusterSize - 1) + nextOffsets[dirB * 2] / 2f), + y2 = (moveDirs[dirB * 2 + 1] * averageB + (clusterB / cwidth) * clusterSize + offsets[dirB * 2 + 1] * (clusterSize - 1) + nextOffsets[dirB * 2 + 1] / 2f); + + return Math.abs(x1 - x2) + Math.abs(y1 - y2); + } + + @Nullable IntSeq clusterAstar(int pathCost, int startNodeIndex, int endNodeIndex){ + frontier.clear(); + costs.clear(); + + costs.put(startNodeIndex, 0); + frontier.add(endNodeIndex, 0); + cameFrom.clear(); + + boolean foundEnd = false; + + while(frontier.size > 0){ + int current = frontier.poll(); + + if(current == endNodeIndex){ + foundEnd = true; + break; + } + + //tmpEdges holds intra edges + tmpEdges.clear(); + + int cluster = NodeIndex.cluster(current), dir = NodeIndex.dir(current), portal = NodeIndex.portal(current); + int cx = cluster % wwidth, cy = cluster / wwidth; + Cluster clust = clusters[pathCost][cluster]; + LongSeq innerCons = clust.portalConnections[dir][portal]; + + //edges for the cluster the node is 'in' + if(innerCons != null){ + checkEdges(pathCost, current, cx, cy, innerCons); + } + + int nextCx = cx + Geometry.d4[dir].x, nextCy = cy + Geometry.d4[dir].y; + if(nextCx >= 0 && nextCy >= 0 && nextCx < cwidth && nextCy < cheight){ + int nextClusteri = nextCx + nextCy * cwidth; + Cluster nextCluster = clusters[pathCost][nextClusteri]; + int relativeDir = (dir + 2) % 4; + LongSeq outerCons = nextCluster.portalConnections[relativeDir] == null ? null : nextCluster.portalConnections[relativeDir][portal]; + if(outerCons != null){ + checkEdges(pathCost, current, nextCx, nextCy, outerCons); + } + } + } + + if(foundEnd){ + IntSeq result = new IntSeq(); + + int cur = endNodeIndex; + while(cur != startNodeIndex){ + result.add(cur); + cur = cameFrom.get(cur); + } + + result.reverse(); + + return result; + } + return null; + } + + void checkEdges(int pathCost, int current, int cx, int cy, LongSeq connections){ + for(int i = 0; i < connections.size; i++){ + long con = connections.items[i]; + float cost = IntraEdge.cost(con); + int otherDir = IntraEdge.dir(con), otherPortal = IntraEdge.portal(con); + int next = makeNodeIndex(cx, cy, otherDir, otherPortal); + + float newCost = costs.get(current) + cost; + + if(newCost < costs.get(next, Float.POSITIVE_INFINITY)){ + costs.put(next, newCost); + frontier.add(next, newCost + clusterNodeHeuristic(pathCost, current, next)); + cameFrom.put(next, current); + } + } + } + Cluster cluster(int pathCost, int cx, int cy){ return clusters[pathCost][cx + cwidth * cy]; } @@ -434,4 +555,14 @@ public class HierarchyPathFinder{ float cost; } + + @Struct + static class NodeIndexStruct{ + @StructField(22) + int cluster; + @StructField(2) + int dir; + @StructField(8) + int portal; + } } From d6d9a52ef9c8715f95479f4f7739c994ba797516 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 6 Nov 2023 22:51:47 -0500 Subject: [PATCH 005/348] It works, but badly --- .../src/mindustry/ai/HierarchyPathFinder.java | 168 +++++++++++++++--- core/src/mindustry/content/Fx.java | 2 +- 2 files changed, 140 insertions(+), 30 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 908bcb5de8..0f5ef0fe48 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -9,10 +9,12 @@ import arc.struct.*; import arc.util.*; import mindustry.annotations.Annotations.*; import mindustry.content.*; +import mindustry.core.*; import mindustry.game.EventType.*; import mindustry.game.*; import mindustry.gen.*; import mindustry.graphics.*; +import mindustry.ui.*; import static mindustry.Vars.*; import static mindustry.ai.Pathfinder.*; @@ -49,14 +51,14 @@ public class HierarchyPathFinder{ int cwidth, cheight; + //TODO: make thread-local (they are dereferenced rarely anyway) static PathfindQueue frontier = new PathfindQueue(); //node index -> total cost static IntFloatMap costs = new IntFloatMap(); // static IntSet usedEdges = new IntSet(); - + static IntSeq bfsQueue = new IntSeq(); static LongSeq tmpEdges = new LongSeq(); - //node index (NodeIndex struct) -> node it came from static IntIntMap cameFrom = new IntIntMap(); @@ -94,7 +96,9 @@ public class HierarchyPathFinder{ for(int cy = 0; cy < cheight; cy++){ var cluster = clusters[cost][cy * cwidth + cx]; if(cluster != null){ - Draw.color(Color.green); + Lines.stroke(0.5f); + Draw.color(Color.gray); + Lines.stroke(1f); Lines.rect(cx * clusterSize * tilesize - tilesize/2f, cy * clusterSize * tilesize - tilesize/2f, clusterSize * tilesize, clusterSize * tilesize); @@ -110,13 +114,13 @@ public class HierarchyPathFinder{ portalToVec(cluster, cx, cy, d, i, Tmp.v1); - Draw.color(Color.red); + Draw.color(Color.brown); Lines.ellipse(30, Tmp.v1.x, Tmp.v1.y, width / 2f, height / 2f, d * 90f - 90f); LongSeq connections = cluster.portalConnections[d] == null ? null : cluster.portalConnections[d][i]; if(connections != null){ - Draw.color(Color.magenta); + Draw.color(Color.forest); for(int coni = 0; coni < connections.size; coni ++){ long con = connections.items[coni]; @@ -150,21 +154,50 @@ public class HierarchyPathFinder{ } } } + + Lines.stroke(3f); + Draw.color(Color.orange); + int node = findClosestNode(Team.sharded.id, 0, player.tileX(), player.tileY()); + int dest = findClosestNode(Team.sharded.id, 0, World.toTile(Core.input.mouseWorldX()), World.toTile(Core.input.mouseWorldY())); + if(node != Integer.MAX_VALUE && dest != Integer.MAX_VALUE){ + var result = clusterAstar(0, node, dest); + if(result != null){ + for(int i = -1; i < result.size - 1; i++){ + int current = i == -1 ? node : result.items[i], next = result.items[i + 1]; + portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), Tmp.v1); + portalToVec(0, NodeIndex.cluster(next), NodeIndex.dir(next), NodeIndex.portal(next), Tmp.v2); + Lines.line(Tmp.v1.x, Tmp.v1.y, Tmp.v2.x, Tmp.v2.y); + } + } + + nodeToVec(dest, Tmp.v1); + Fonts.outline.draw(clusterNodeHeuristic(0, node, dest) + "", Tmp.v1.x, Tmp.v1.y); + } + Draw.reset(); }); }); } } - void portalToVec(Cluster cluster, int cx, int cy, int d, int i, Vec2 out){ - int pos = cluster.portals[d].items[i]; + Vec2 nodeToVec(int current, Vec2 out){ + portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), out); + return out; + } + + void portalToVec(int pathCost, int cluster, int direction, int portalIndex, Vec2 out){ + portalToVec(clusters[pathCost][cluster], cluster % cwidth, cluster / cwidth, direction, portalIndex, out); + } + + void portalToVec(Cluster cluster, int cx, int cy, int direction, int portalIndex, Vec2 out){ + int pos = cluster.portals[direction].items[portalIndex]; int from = Point2.x(pos), to = Point2.y(pos); - int addX = moveDirs[d * 2], addY = moveDirs[d * 2 + 1]; + int addX = moveDirs[direction * 2], addY = moveDirs[direction * 2 + 1]; float average = (from + to) / 2f; float - x = (addX * average + cx * clusterSize + offsets[d * 2] * (clusterSize - 1) + nextOffsets[d * 2] / 2f) * tilesize, - y = (addY * average + cy * clusterSize + offsets[d * 2 + 1] * (clusterSize - 1) + nextOffsets[d * 2 + 1] / 2f) * tilesize; + x = (addX * average + cx * clusterSize + offsets[direction * 2] * (clusterSize - 1) + nextOffsets[direction * 2] / 2f) * tilesize, + y = (addY * average + cy * clusterSize + offsets[direction * 2 + 1] * (clusterSize - 1) + nextOffsets[direction * 2 + 1] / 2f) * tilesize; out.set(x, y); } @@ -260,7 +293,7 @@ public class HierarchyPathFinder{ //TODO: how the hell to identify a vertex? //cluster (i16) | direction (i2) | index (i14) - //TODO: clear portal connections. also share them? + //TODO: clear portal connections for(int direction = 0; direction < 4; direction++){ var portals = cluster.portals[direction]; @@ -296,7 +329,7 @@ public class HierarchyPathFinder{ otherX = (moveDirs[otherDir * 2] * otherAverage + ox), otherY = (moveDirs[otherDir * 2 + 1] * otherAverage + oy); - //HOW (redundant nodes?) + //duplicate portal; should never happen. if(Point2.pack(x, y) == Point2.pack(otherX, otherY)){ continue; } @@ -321,8 +354,6 @@ public class HierarchyPathFinder{ //TODO: can there be duplicate edges?? cluster.portalConnections[direction][i].add(IntraEdge.get(otherDir, j, connectionCost)); cluster.portalConnections[otherDir][j].add(IntraEdge.get(direction, i, connectionCost)); - - //Fx.debugLine.at(x* tilesize, y * tilesize, 0f, Color.purple, new Vec2[]{new Vec2(x, y).scl(tilesize), new Vec2(otherX, otherY).scl(tilesize)}); } } } @@ -351,6 +382,7 @@ public class HierarchyPathFinder{ frontier.clear(); costs.clear(); + //TODO: this can be faster and more memory efficient by making costs a NxN array... probably? costs.put(startPos, 0); frontier.add(startPos, 0); @@ -385,13 +417,10 @@ public class HierarchyPathFinder{ float newCost = costs.get(current) + add; - //a cost of 0 means "not set" if(newCost < costs.get(next, Float.POSITIVE_INFINITY)){ costs.put(next, newCost); float priority = newCost + heuristic(next, goalPos); frontier.add(next, priority); - - //cameFrom.put(next, current); } } } @@ -413,9 +442,74 @@ public class HierarchyPathFinder{ dir = 1; cy --; } + return NodeIndex.get(cx + cy * cwidth, dir, portal); } + //uses BFS to find the closest node index to specified coordinates + //this node is used in cluster A* + /** @return MAX_VALUE if no node is found */ + private int findClosestNode(int team, int pathCost, int tileX, int tileY){ + int cx = tileX / clusterSize, cy = tileY / clusterSize; + + if(cx < 0 || cy < 0 || cx >= cwidth || cy >= cheight){ + return Integer.MAX_VALUE; + } + + //TODO + PathCost cost = ControlPathfinder.costGround; + + Cluster cluster = clusters[pathCost][cx + cy * cwidth]; + int minX = cx * clusterSize, minY = cy * clusterSize, maxX = Math.min(minX + clusterSize - 1, wwidth - 1), maxY = Math.min(minY + clusterSize - 1, wheight - 1); + + int bestPortalPair = Integer.MAX_VALUE; + float bestCost = Float.MAX_VALUE; + + if(cluster != null){ //TODO create on demand?? + + //A* to every node, find the best one (I know there's a better algorithm for this, probably dijkstra) + for(int dir = 0; dir < 4; dir++){ + var portals = cluster.portals[dir]; + if(portals == null) continue; + + for(int j = 0; j < portals.size; j++){ + + int + other = portals.items[j], + otherFrom = Point2.x(other), otherTo = Point2.y(other), + otherAverage = (otherFrom + otherTo) / 2, + ox = cx * clusterSize + offsets[dir * 2] * (clusterSize - 1), + oy = cy * clusterSize + offsets[dir * 2 + 1] * (clusterSize - 1), + otherX = (moveDirs[dir * 2] * otherAverage + ox), + otherY = (moveDirs[dir * 2 + 1] * otherAverage + oy); + + float connectionCost = innerAstar( + team, cost, + minX, minY, maxX, maxY, + tileX + tileY * wwidth, + otherX + otherY * wwidth, + (moveDirs[dir * 2] * otherFrom + ox), + (moveDirs[dir * 2 + 1] * otherFrom + oy), + (moveDirs[dir * 2] * otherTo + ox), + (moveDirs[dir * 2 + 1] * otherTo + oy) + ); + + //better cost found, update and return + if(connectionCost != -1f && connectionCost < bestCost){ + bestPortalPair = Point2.pack(dir, j); + bestCost = connectionCost; + } + } + } + + if(bestPortalPair != Integer.MAX_VALUE){ + return makeNodeIndex(cx, cy, Point2.x(bestPortalPair), Point2.y(bestPortalPair)); + } + } + + return Integer.MAX_VALUE; + } + //distance heuristic: manhattan private float clusterNodeHeuristic(int pathCost, int nodeA, int nodeB){ int @@ -424,10 +518,9 @@ public class HierarchyPathFinder{ portalA = NodeIndex.portal(nodeA), clusterB = NodeIndex.cluster(nodeB), dirB = NodeIndex.dir(nodeB), - portalB = NodeIndex.portal(nodeB); - - int rangeA = clusters[pathCost][clusterA].portals[dirA].items[portalA]; - int rangeB = clusters[pathCost][clusterB].portals[dirB].items[portalB]; + portalB = NodeIndex.portal(nodeB), + rangeA = clusters[pathCost][clusterA].portals[dirA].items[portalA], + rangeB = clusters[pathCost][clusterB].portals[dirB].items[portalB]; float averageA = (Point2.x(rangeA) + Point2.y(rangeA)) / 2f, @@ -442,13 +535,24 @@ public class HierarchyPathFinder{ } @Nullable IntSeq clusterAstar(int pathCost, int startNodeIndex, int endNodeIndex){ + var v1 = nodeToVec(startNodeIndex, Tmp.v1); + var v2 = nodeToVec(endNodeIndex, Tmp.v2); + Fx.placeBlock.at(v1.x, v1.y, 1); + Fx.placeBlock.at(v2.x, v2.y, 1); + + if(startNodeIndex == endNodeIndex){ + //TODO alloc + return IntSeq.with(startNodeIndex); + } + frontier.clear(); costs.clear(); - - costs.put(startNodeIndex, 0); - frontier.add(endNodeIndex, 0); cameFrom.clear(); + cameFrom.put(startNodeIndex, startNodeIndex); + costs.put(startNodeIndex, 0); + frontier.add(startNodeIndex, 0); + boolean foundEnd = false; while(frontier.size > 0){ @@ -459,19 +563,17 @@ public class HierarchyPathFinder{ break; } - //tmpEdges holds intra edges - tmpEdges.clear(); - int cluster = NodeIndex.cluster(current), dir = NodeIndex.dir(current), portal = NodeIndex.portal(current); - int cx = cluster % wwidth, cy = cluster / wwidth; + int cx = cluster % cwidth, cy = cluster / cwidth; Cluster clust = clusters[pathCost][cluster]; - LongSeq innerCons = clust.portalConnections[dir][portal]; + LongSeq innerCons = clust.portalConnections[dir] == null || portal >= clust.portalConnections[dir].length ? null : clust.portalConnections[dir][portal]; //edges for the cluster the node is 'in' if(innerCons != null){ checkEdges(pathCost, current, cx, cy, innerCons); } + //edges that this node 'faces' from the other side int nextCx = cx + Geometry.d4[dir].x, nextCy = cy + Geometry.d4[dir].y; if(nextCx >= 0 && nextCy >= 0 && nextCx < cwidth && nextCy < cheight){ int nextClusteri = nextCx + nextCy * cwidth; @@ -500,6 +602,10 @@ public class HierarchyPathFinder{ return null; } + static void line(Vec2 a, Vec2 b){ + Fx.debugLine.at(a.x, a.y, 0f, Color.blue.cpy().a(0.1f), new Vec2[]{a.cpy(), b.cpy()}); + } + void checkEdges(int pathCost, int current, int cx, int cy, LongSeq connections){ for(int i = 0; i < connections.size; i++){ long con = connections.items[i]; @@ -511,8 +617,12 @@ public class HierarchyPathFinder{ if(newCost < costs.get(next, Float.POSITIVE_INFINITY)){ costs.put(next, newCost); + frontier.add(next, newCost + clusterNodeHeuristic(pathCost, current, next)); cameFrom.put(next, current); + + //TODO debug + line(nodeToVec(current, Tmp.v1), nodeToVec(next, Tmp.v2)); } } } diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index 944bd87168..953274f8a1 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -2584,7 +2584,7 @@ public class Fx{ if(!(e.data instanceof Vec2[] vec)) return; Draw.color(e.color); - Lines.stroke(1f); + Lines.stroke(2f); if(vec.length == 2){ Lines.line(vec[0].x, vec[0].y, vec[1].x, vec[1].y); From a17dcbca5aee0147cfb70d331e734984030c4028 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 6 Nov 2023 23:02:26 -0500 Subject: [PATCH 006/348] Heuristic fixed --- core/src/mindustry/ai/HierarchyPathFinder.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 0f5ef0fe48..eafe467bc3 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -570,7 +570,7 @@ public class HierarchyPathFinder{ //edges for the cluster the node is 'in' if(innerCons != null){ - checkEdges(pathCost, current, cx, cy, innerCons); + checkEdges(pathCost, current, endNodeIndex, cx, cy, innerCons); } //edges that this node 'faces' from the other side @@ -581,7 +581,7 @@ public class HierarchyPathFinder{ int relativeDir = (dir + 2) % 4; LongSeq outerCons = nextCluster.portalConnections[relativeDir] == null ? null : nextCluster.portalConnections[relativeDir][portal]; if(outerCons != null){ - checkEdges(pathCost, current, nextCx, nextCy, outerCons); + checkEdges(pathCost, current, endNodeIndex, nextCx, nextCy, outerCons); } } } @@ -606,7 +606,7 @@ public class HierarchyPathFinder{ Fx.debugLine.at(a.x, a.y, 0f, Color.blue.cpy().a(0.1f), new Vec2[]{a.cpy(), b.cpy()}); } - void checkEdges(int pathCost, int current, int cx, int cy, LongSeq connections){ + void checkEdges(int pathCost, int current, int goal, int cx, int cy, LongSeq connections){ for(int i = 0; i < connections.size; i++){ long con = connections.items[i]; float cost = IntraEdge.cost(con); @@ -618,7 +618,7 @@ public class HierarchyPathFinder{ if(newCost < costs.get(next, Float.POSITIVE_INFINITY)){ costs.put(next, newCost); - frontier.add(next, newCost + clusterNodeHeuristic(pathCost, current, next)); + frontier.add(next, newCost + clusterNodeHeuristic(pathCost, next, goal)); cameFrom.put(next, current); //TODO debug From 0186c35a1a5d77b3b28a267dc937ef3360bc9e41 Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 7 Nov 2023 00:11:44 -0500 Subject: [PATCH 007/348] Flowfield tests --- .../src/mindustry/ai/HierarchyPathFinder.java | 93 ++++++++++++++++++- 1 file changed, 90 insertions(+), 3 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index eafe467bc3..3989f949b5 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -162,12 +162,36 @@ public class HierarchyPathFinder{ if(node != Integer.MAX_VALUE && dest != Integer.MAX_VALUE){ var result = clusterAstar(0, node, dest); if(result != null){ + for(int i = -1; i < result.size - 1; i++){ + int endCluster = NodeIndex.cluster(result.items[i + 1]); + int cx = endCluster % cwidth, cy = endCluster / cwidth; + int[] field = flowField(0, cx, cy, 0, dest); + + for(int y = 0; y < clusterSize; y++){ + for(int x = 0; x < clusterSize; x++){ + int value = field[x + y *clusterSize]; + Tmp.c1.a = 1f; + Lines.stroke(0.8f, Tmp.c1.fromHsv(value * 3f, 1f, 1f)); + Draw.alpha(0.5f); + Lines.rect((x + cx * clusterSize) * tilesize - tilesize/2f, (y + cy * clusterSize) * tilesize - tilesize/2f, tilesize, tilesize); + } + } + } + + Lines.stroke(3f); + Draw.color(Color.orange); + for(int i = -1; i < result.size - 1; i++){ int current = i == -1 ? node : result.items[i], next = result.items[i + 1]; portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), Tmp.v1); portalToVec(0, NodeIndex.cluster(next), NodeIndex.dir(next), NodeIndex.portal(next), Tmp.v2); Lines.line(Tmp.v1.x, Tmp.v1.y, Tmp.v2.x, Tmp.v2.y); } + + + + + //flowField(0, ) } nodeToVec(dest, Tmp.v1); @@ -446,7 +470,7 @@ public class HierarchyPathFinder{ return NodeIndex.get(cx + cy * cwidth, dir, portal); } - //uses BFS to find the closest node index to specified coordinates + //uses A* to find the closest node index to specified coordinates //this node is used in cluster A* /** @return MAX_VALUE if no node is found */ private int findClosestNode(int team, int pathCost, int tileX, int tileY){ @@ -488,6 +512,7 @@ public class HierarchyPathFinder{ minX, minY, maxX, maxY, tileX + tileY * wwidth, otherX + otherY * wwidth, + //TODO these are wrong and never actually trigger (moveDirs[dir * 2] * otherFrom + ox), (moveDirs[dir * 2 + 1] * otherFrom + oy), (moveDirs[dir * 2] * otherTo + ox), @@ -627,8 +652,70 @@ public class HierarchyPathFinder{ } } - Cluster cluster(int pathCost, int cx, int cy){ - return clusters[pathCost][cx + cwidth * cy]; + //both nodes must be inside the same flow field (cx, cy) + int[] flowField(int pathCost, int cx, int cy, int nodeFrom, int nodeTo){ + + Cluster cluster = clusters[pathCost][cx + cy * cwidth]; + + int[] weights = new int[clusterSize * clusterSize]; + byte[] searches = new byte[clusterSize * clusterSize]; + PathCost pcost = ControlPathfinder.costGround; + int team = Team.sharded.id; + + IntQueue frontier = new IntQueue(); + int search = 1; + + //TODO actually set up the frontier and destinations (where are you going?) + { + int + dir = NodeIndex.dir(nodeTo), + other = cluster.portals[dir].items[NodeIndex.portal(nodeTo)], + otherFrom = Point2.x(other), otherTo = Point2.y(other), + ox = cx * clusterSize + offsets[dir * 2] * (clusterSize - 1), + oy = cy * clusterSize + offsets[dir * 2 + 1] * (clusterSize - 1), + + maxX = (moveDirs[dir * 2] * otherFrom + ox), + maxY = (moveDirs[dir * 2 + 1] * otherFrom + oy), + minX = (moveDirs[dir * 2] * otherTo + ox), + minY = (moveDirs[dir * 2 + 1] * otherTo + oy) + + ; + + //TODO: being zero INSIDE the cluster means that the unit will stop at the edge and not move between clusters - bad! + for(int x = minX; x <= maxX; x++){ + for(int y = minY; y <= maxY; y++){ + frontier.addFirst(x + y * wwidth); + } + } + } + + int minX = cx * clusterSize, minY = cy * clusterSize, maxX = Math.min(minX + clusterSize - 1, wwidth - 1), maxY = Math.min(minY + clusterSize - 1, wheight - 1); + //TODO spread this out across many frames + while(frontier.size > 0){ + int tile = frontier.removeLast(); + int baseX = tile % wwidth, baseY = tile / wwidth; + int cost = weights[(baseX - minX) + (baseY - minY) * clusterSize]; + + if(cost != impassable){ + for(Point2 point : Geometry.d4){ + + int dx = baseX + point.x, dy = baseY + point.y; + + if(dx < minX || dy < minY || dx > maxX || dy > maxY) continue; + + int newPos = tile + point.x + point.y * wwidth; + int newPosArray = (dx - minX) + (dy - minY) * clusterSize; + int otherCost = pcost.getCost(team, pathfinder.tiles[newPos]); + + if((weights[newPosArray] > cost + otherCost || searches[newPosArray] < search) && otherCost != impassable){ + frontier.addFirst(newPos); + weights[newPosArray] = cost + otherCost; + searches[newPosArray] = (byte)search; + } + } + } + } + return weights; } private static boolean solid(int team, PathCost type, int x, int y){ From 65d0b6adccc0685b8e7dc4ec04b09f4c70e3b535 Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 7 Nov 2023 10:56:17 -0500 Subject: [PATCH 008/348] progress --- .../src/mindustry/ai/HierarchyPathFinder.java | 194 +++++++++++++----- core/src/mindustry/ai/types/CommandAI.java | 2 +- gradle.properties | 2 +- 3 files changed, 144 insertions(+), 54 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 3989f949b5..012c0495e4 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -15,6 +15,7 @@ import mindustry.game.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.ui.*; +import mindustry.world.*; import static mindustry.Vars.*; import static mindustry.ai.Pathfinder.*; @@ -155,50 +156,54 @@ public class HierarchyPathFinder{ } } - Lines.stroke(3f); - Draw.color(Color.orange); - int node = findClosestNode(Team.sharded.id, 0, player.tileX(), player.tileY()); - int dest = findClosestNode(Team.sharded.id, 0, World.toTile(Core.input.mouseWorldX()), World.toTile(Core.input.mouseWorldY())); - if(node != Integer.MAX_VALUE && dest != Integer.MAX_VALUE){ - var result = clusterAstar(0, node, dest); - if(result != null){ - for(int i = -1; i < result.size - 1; i++){ - int endCluster = NodeIndex.cluster(result.items[i + 1]); - int cx = endCluster % cwidth, cy = endCluster / cwidth; - int[] field = flowField(0, cx, cy, 0, dest); + if(false){ + Lines.stroke(3f); + Draw.color(Color.orange); + int node = findClosestNode(Team.sharded.id, 0, player.tileX(), player.tileY()); + int dest = findClosestNode(Team.sharded.id, 0, World.toTile(Core.input.mouseWorldX()), World.toTile(Core.input.mouseWorldY())); + if(node != Integer.MAX_VALUE && dest != Integer.MAX_VALUE){ + var result = clusterAstar(0, node, dest); + if(result != null){ + for(int i = -1; i < result.size - 1; i++){ + int endCluster = NodeIndex.cluster(result.items[i + 1]); + int cx = endCluster % cwidth, cy = endCluster / cwidth; + int[] field = flowField(0, cx, cy, 0, dest, World.toTile(Core.input.mouseWorldX()), World.toTile(Core.input.mouseWorldY())); - for(int y = 0; y < clusterSize; y++){ - for(int x = 0; x < clusterSize; x++){ - int value = field[x + y *clusterSize]; - Tmp.c1.a = 1f; - Lines.stroke(0.8f, Tmp.c1.fromHsv(value * 3f, 1f, 1f)); - Draw.alpha(0.5f); - Lines.rect((x + cx * clusterSize) * tilesize - tilesize/2f, (y + cy * clusterSize) * tilesize - tilesize/2f, tilesize, tilesize); + for(int y = 0; y < clusterSize; y++){ + for(int x = 0; x < clusterSize; x++){ + int value = field[x + y *clusterSize]; + Tmp.c1.a = 1f; + Lines.stroke(0.8f, Tmp.c1.fromHsv(value * 3f, 1f, 1f)); + Draw.alpha(0.5f); + Lines.rect((x + cx * clusterSize) * tilesize - tilesize/2f, (y + cy * clusterSize) * tilesize - tilesize/2f, tilesize, tilesize); + } } } + + Lines.stroke(3f); + Draw.color(Color.orange); + + for(int i = -1; i < result.size - 1; i++){ + int current = i == -1 ? node : result.items[i], next = result.items[i + 1]; + portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), Tmp.v1); + portalToVec(0, NodeIndex.cluster(next), NodeIndex.dir(next), NodeIndex.portal(next), Tmp.v2); + Lines.line(Tmp.v1.x, Tmp.v1.y, Tmp.v2.x, Tmp.v2.y); + } + + + + + //flowField(0, ) } - Lines.stroke(3f); - Draw.color(Color.orange); - - for(int i = -1; i < result.size - 1; i++){ - int current = i == -1 ? node : result.items[i], next = result.items[i + 1]; - portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), Tmp.v1); - portalToVec(0, NodeIndex.cluster(next), NodeIndex.dir(next), NodeIndex.portal(next), Tmp.v2); - Lines.line(Tmp.v1.x, Tmp.v1.y, Tmp.v2.x, Tmp.v2.y); - } - - - - - //flowField(0, ) + nodeToVec(dest, Tmp.v1); + Fonts.outline.draw(clusterNodeHeuristic(0, node, dest) + "", Tmp.v1.x, Tmp.v1.y); } - nodeToVec(dest, Tmp.v1); - Fonts.outline.draw(clusterNodeHeuristic(0, node, dest) + "", Tmp.v1.x, Tmp.v1.y); + Draw.reset(); } - Draw.reset(); + }); }); } @@ -631,6 +636,10 @@ public class HierarchyPathFinder{ Fx.debugLine.at(a.x, a.y, 0f, Color.blue.cpy().a(0.1f), new Vec2[]{a.cpy(), b.cpy()}); } + static void line(Vec2 a, Vec2 b, Color color){ + Fx.debugLine.at(a.x, a.y, 0f, color, new Vec2[]{a.cpy(), b.cpy()}); + } + void checkEdges(int pathCost, int current, int goal, int cx, int cy, LongSeq connections){ for(int i = 0; i < connections.size; i++){ long con = connections.items[i]; @@ -653,48 +662,63 @@ public class HierarchyPathFinder{ } //both nodes must be inside the same flow field (cx, cy) - int[] flowField(int pathCost, int cx, int cy, int nodeFrom, int nodeTo){ + int[] flowField(int pathCost, int cx, int cy, int nodeFrom, int nodeTo, int goalX, int goalY){ Cluster cluster = clusters[pathCost][cx + cy * cwidth]; - int[] weights = new int[clusterSize * clusterSize]; - byte[] searches = new byte[clusterSize * clusterSize]; + int realSize = clusterSize + 2; + + int[] weights = new int[realSize * realSize]; + byte[] searches = new byte[realSize * realSize]; PathCost pcost = ControlPathfinder.costGround; int team = Team.sharded.id; IntQueue frontier = new IntQueue(); int search = 1; - //TODO actually set up the frontier and destinations (where are you going?) - { + int + minX = cx * clusterSize - 1, + minY = cy * clusterSize - 1, + maxX = Math.min(minX + clusterSize + 1, wwidth - 1), + maxY = Math.min(minY + clusterSize + 1, wheight - 1), + toCluster = NodeIndex.cluster(nodeTo), + tocx = toCluster % cwidth, + tocy = toCluster / cwidth; + + //you're at the cluster with the goal node + if(goalX / clusterSize == cx && goalY / clusterSize == cy){ + frontier.addFirst(goalX + goalY * wwidth); + }else{ int dir = NodeIndex.dir(nodeTo), other = cluster.portals[dir].items[NodeIndex.portal(nodeTo)], otherFrom = Point2.x(other), otherTo = Point2.y(other), - ox = cx * clusterSize + offsets[dir * 2] * (clusterSize - 1), - oy = cy * clusterSize + offsets[dir * 2 + 1] * (clusterSize - 1), + ox = tocx * clusterSize + offsets[dir * 2] * (clusterSize - 1), + oy = tocy * clusterSize + offsets[dir * 2 + 1] * (clusterSize - 1), - maxX = (moveDirs[dir * 2] * otherFrom + ox), - maxY = (moveDirs[dir * 2 + 1] * otherFrom + oy), - minX = (moveDirs[dir * 2] * otherTo + ox), - minY = (moveDirs[dir * 2 + 1] * otherTo + oy) + px2 = Mathf.clamp((moveDirs[dir * 2] * otherFrom + ox), minX, maxX), + py2 = Mathf.clamp((moveDirs[dir * 2 + 1] * otherFrom + oy), minY, maxY), + px1 = Mathf.clamp((moveDirs[dir * 2] * otherTo + ox), minX, maxX), + py1 = Mathf.clamp((moveDirs[dir * 2 + 1] * otherTo + oy), minY, maxY); - ; + if(px1 >= cx * clusterSize && px2 < cx * clusterSize + clusterSize && py1 >= cy * clusterSize && py2 < cy * clusterSize){ + Log.info("inside the box"); //TODO broken + } //TODO: being zero INSIDE the cluster means that the unit will stop at the edge and not move between clusters - bad! - for(int x = minX; x <= maxX; x++){ - for(int y = minY; y <= maxY; y++){ + for(int x = px1; x <= px2; x++){ + for(int y = py1; y <= py2; y++){ frontier.addFirst(x + y * wwidth); + Fx.lightBlock.at(x * tilesize, y * tilesize, 1f, Color.orange); } } } - int minX = cx * clusterSize, minY = cy * clusterSize, maxX = Math.min(minX + clusterSize - 1, wwidth - 1), maxY = Math.min(minY + clusterSize - 1, wheight - 1); //TODO spread this out across many frames while(frontier.size > 0){ int tile = frontier.removeLast(); int baseX = tile % wwidth, baseY = tile / wwidth; - int cost = weights[(baseX - minX) + (baseY - minY) * clusterSize]; + int cost = weights[(baseX - minX) + (baseY - minY) * realSize]; if(cost != impassable){ for(Point2 point : Geometry.d4){ @@ -704,7 +728,7 @@ public class HierarchyPathFinder{ if(dx < minX || dy < minY || dx > maxX || dy > maxY) continue; int newPos = tile + point.x + point.y * wwidth; - int newPosArray = (dx - minX) + (dy - minY) * clusterSize; + int newPosArray = (dx - minX) + (dy - minY) * realSize; int otherCost = pcost.getCost(team, pathfinder.tiles[newPos]); if((weights[newPosArray] > cost + otherCost || searches[newPosArray] < search) && otherCost != impassable){ @@ -718,6 +742,72 @@ public class HierarchyPathFinder{ return weights; } + public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 out, boolean[] noResultFound){ + int cost = 0; + + int node = findClosestNode(unit.team.id, cost, unit.tileX(), unit.tileY()); + int dest = findClosestNode(unit.team.id, cost, World.toTile(destination.x), World.toTile(destination.y)); + + var result = clusterAstar(cost, node, dest); + Tile tile = unit.tileOn(); + if(result != null){ + for(int i = -1; i < result.size - 1; i++){ + int current = i == -1 ? node : result.items[i], next = result.items[i + 1]; + portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), Tmp.v1); + portalToVec(0, NodeIndex.cluster(next), NodeIndex.dir(next), NodeIndex.portal(next), Tmp.v2); + line(Tmp.v1, Tmp.v2, Color.orange); + } + + int cx = unit.tileX() / clusterSize, cy = unit.tileY() / clusterSize, + ox = cx * clusterSize - 1, oy = cy * clusterSize - 1; + + int nextNode = result.items[0]; + + int[] field = flowField(cost, cx, cy, node, nextNode, World.toTile(destination.x), World.toTile(destination.y)); + + if(field != null && tile != null){ + int value = field[(tile.x - ox) + (tile.y - oy) * (clusterSize + 2)]; + + Tile current = null; + int tl = 0; + for(Point2 point : Geometry.d8){ + int dx = tile.x + point.x, dy = tile.y + point.y; + + Tile other = world.tile(dx, dy); + + + if(other == null || dx < ox || dy < oy || dx >= ox + clusterSize + 2 || dy >= oy + clusterSize + 2) continue; + + int local = (dx - ox) + (dy - oy) * (clusterSize + 2); + int packed = world.packArray(dx, dy); + int otherCost = field[local]; + + if(otherCost < value && (current == null || otherCost < tl) && passable(ControlPathfinder.costGround, unit.team.id, packed) && + !(point.x != 0 && point.y != 0 && (!passable(ControlPathfinder.costGround, unit.team.id, world.packArray(tile.x + point.x, tile.y)) || + (!passable(ControlPathfinder.costGround, unit.team.id, world.packArray(tile.x, tile.y + point.y)))))){ //diagonal corner trap + + current = other; + tl = field[local]; + } + } + + if(!(current == null || tl == impassable || (cost == costGround && current.dangerous() && !tile.dangerous()))){ + out.set(current); + return true; + } + } + } + + noResultFound[0] = true; + return false; + } + + private static boolean passable(PathCost cost, int team, int pos){ + int amount = cost.getCost(team, pathfinder.tiles[pos]); + //edge case: naval reports costs of 6000+ for non-liquids, even though they are not technically passable + return amount != impassable && !(cost == costTypes.get(costNaval) && amount >= 6000); + } + private static boolean solid(int team, PathCost type, int x, int y){ return x < 0 || y < 0 || x >= wwidth || y >= wheight || solid(team, type, x + y * wwidth, true); } diff --git a/core/src/mindustry/ai/types/CommandAI.java b/core/src/mindustry/ai/types/CommandAI.java index a44b5b457c..86a5a89447 100644 --- a/core/src/mindustry/ai/types/CommandAI.java +++ b/core/src/mindustry/ai/types/CommandAI.java @@ -247,7 +247,7 @@ public class CommandAI extends AIController{ } //if you've spent 3 seconds stuck, something is wrong, move regardless - move = Vars.controlPath.getPathPosition(unit, pathId, vecMovePos, vecOut, noFound) && (!blockingUnit || timeSpentBlocked > maxBlockTime); + move = hpath.getPathPosition(unit, pathId, vecMovePos, vecOut, noFound) && (!blockingUnit || timeSpentBlocked > maxBlockTime); //we've reached the final point if the returned coordinate is equal to the supplied input isFinalPoint &= vecMovePos.epsilonEquals(vecOut, 4.1f); diff --git a/gradle.properties b/gradle.properties index f9fa0851e3..090150dba9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=96f4f4214a +archash=e2fdbab477 From be44e283d8abec55bd9a1c5fc702d3065353e2bb Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 8 Nov 2023 08:42:48 -0500 Subject: [PATCH 009/348] progress --- .../src/mindustry/ai/HierarchyPathFinder.java | 259 +++++++++--------- core/src/mindustry/content/Fx.java | 10 + 2 files changed, 142 insertions(+), 127 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 012c0495e4..8f0b500c99 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -53,15 +53,13 @@ public class HierarchyPathFinder{ int cwidth, cheight; //TODO: make thread-local (they are dereferenced rarely anyway) - static PathfindQueue frontier = new PathfindQueue(); + PathfindQueue frontier = new PathfindQueue(); //node index -> total cost - static IntFloatMap costs = new IntFloatMap(); - // - static IntSet usedEdges = new IntSet(); - static IntSeq bfsQueue = new IntSeq(); - static LongSeq tmpEdges = new LongSeq(); + IntFloatMap costs = new IntFloatMap(); + IntSet usedEdges = new IntSet(); //node index (NodeIndex struct) -> node it came from - static IntIntMap cameFrom = new IntIntMap(); + IntIntMap cameFrom = new IntIntMap(); + IntMap fields; public HierarchyPathFinder(){ @@ -164,21 +162,6 @@ public class HierarchyPathFinder{ if(node != Integer.MAX_VALUE && dest != Integer.MAX_VALUE){ var result = clusterAstar(0, node, dest); if(result != null){ - for(int i = -1; i < result.size - 1; i++){ - int endCluster = NodeIndex.cluster(result.items[i + 1]); - int cx = endCluster % cwidth, cy = endCluster / cwidth; - int[] field = flowField(0, cx, cy, 0, dest, World.toTile(Core.input.mouseWorldX()), World.toTile(Core.input.mouseWorldY())); - - for(int y = 0; y < clusterSize; y++){ - for(int x = 0; x < clusterSize; x++){ - int value = field[x + y *clusterSize]; - Tmp.c1.a = 1f; - Lines.stroke(0.8f, Tmp.c1.fromHsv(value * 3f, 1f, 1f)); - Draw.alpha(0.5f); - Lines.rect((x + cx * clusterSize) * tilesize - tilesize/2f, (y + cy * clusterSize) * tilesize - tilesize/2f, tilesize, tilesize); - } - } - } Lines.stroke(3f); Draw.color(Color.orange); @@ -189,11 +172,6 @@ public class HierarchyPathFinder{ portalToVec(0, NodeIndex.cluster(next), NodeIndex.dir(next), NodeIndex.portal(next), Tmp.v2); Lines.line(Tmp.v1.x, Tmp.v1.y, Tmp.v2.x, Tmp.v2.y); } - - - - - //flowField(0, ) } nodeToVec(dest, Tmp.v1); @@ -203,6 +181,21 @@ public class HierarchyPathFinder{ Draw.reset(); } + if(fields != null){ + for(var entry : fields){ + int cx = entry.key % cwidth, cy = entry.key / cwidth; + for(int y = 0; y < clusterSize; y++){ + for(int x = 0; x < clusterSize; x++){ + int value = entry.value[x + y * clusterSize]; + Tmp.c1.a = 1f; + Lines.stroke(0.8f, Tmp.c1.fromHsv(value * 3f, 1f, 1f)); + Draw.alpha(0.5f); + Fill.square((x + cx * clusterSize) * tilesize, (y + cy * clusterSize) * tilesize, tilesize/2f); + } + } + } + } + }); }); @@ -415,11 +408,6 @@ public class HierarchyPathFinder{ costs.put(startPos, 0); frontier.add(startPos, 0); - if(debug && false){ - Fx.debugLine.at(Point2.x(startPos) * tilesize, Point2.y(startPos) * tilesize, 0f, Color.purple, - new Vec2[]{new Vec2(Point2.x(startPos), Point2.y(startPos)).scl(tilesize), new Vec2(Point2.x(goalPos), Point2.y(goalPos)).scl(tilesize)}); - } - while(frontier.size > 0){ int current = frontier.poll(); @@ -567,8 +555,6 @@ public class HierarchyPathFinder{ @Nullable IntSeq clusterAstar(int pathCost, int startNodeIndex, int endNodeIndex){ var v1 = nodeToVec(startNodeIndex, Tmp.v1); var v2 = nodeToVec(endNodeIndex, Tmp.v2); - Fx.placeBlock.at(v1.x, v1.y, 1); - Fx.placeBlock.at(v2.x, v2.y, 1); if(startNodeIndex == endNodeIndex){ //TODO alloc @@ -661,147 +647,166 @@ public class HierarchyPathFinder{ } } - //both nodes must be inside the same flow field (cx, cy) - int[] flowField(int pathCost, int cx, int cy, int nodeFrom, int nodeTo, int goalX, int goalY){ + public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 out, boolean[] noResultFound){ + int costId = 0; - Cluster cluster = clusters[pathCost][cx + cy * cwidth]; + int node = findClosestNode(unit.team.id, costId, unit.tileX(), unit.tileY()); + int dest = findClosestNode(unit.team.id, costId, World.toTile(destination.x), World.toTile(destination.y)); - int realSize = clusterSize + 2; + fields = new IntMap<>(); - int[] weights = new int[realSize * realSize]; - byte[] searches = new byte[realSize * realSize]; PathCost pcost = ControlPathfinder.costGround; int team = Team.sharded.id; + int goalPos = (World.toTile(destination.x) + World.toTile(destination.y) * wwidth); IntQueue frontier = new IntQueue(); - int search = 1; + frontier.addFirst(goalPos); - int - minX = cx * clusterSize - 1, - minY = cy * clusterSize - 1, - maxX = Math.min(minX + clusterSize + 1, wwidth - 1), - maxY = Math.min(minY + clusterSize + 1, wheight - 1), - toCluster = NodeIndex.cluster(nodeTo), - tocx = toCluster % cwidth, - tocy = toCluster / cwidth; + Tile tileOn = unit.tileOn(); - //you're at the cluster with the goal node - if(goalX / clusterSize == cx && goalY / clusterSize == cy){ - frontier.addFirst(goalX + goalY * wwidth); - }else{ - int - dir = NodeIndex.dir(nodeTo), - other = cluster.portals[dir].items[NodeIndex.portal(nodeTo)], - otherFrom = Point2.x(other), otherTo = Point2.y(other), - ox = tocx * clusterSize + offsets[dir * 2] * (clusterSize - 1), - oy = tocy * clusterSize + offsets[dir * 2 + 1] * (clusterSize - 1), + var result = clusterAstar(costId, node, dest); + if(result != null && tileOn != null){ - px2 = Mathf.clamp((moveDirs[dir * 2] * otherFrom + ox), minX, maxX), - py2 = Mathf.clamp((moveDirs[dir * 2 + 1] * otherFrom + oy), minY, maxY), - px1 = Mathf.clamp((moveDirs[dir * 2] * otherTo + ox), minX, maxX), - py1 = Mathf.clamp((moveDirs[dir * 2 + 1] * otherTo + oy), minY, maxY); + int fsize = clusterSize * clusterSize; + int cx = unit.tileX() / clusterSize, cy = unit.tileY() / clusterSize; - if(px1 >= cx * clusterSize && px2 < cx * clusterSize + clusterSize && py1 >= cy * clusterSize && py2 < cy * clusterSize){ - Log.info("inside the box"); //TODO broken - } + fields.put(cx + cy * cwidth, new int[fsize]); - //TODO: being zero INSIDE the cluster means that the unit will stop at the edge and not move between clusters - bad! - for(int x = px1; x <= px2; x++){ - for(int y = py1; y <= py2; y++){ - frontier.addFirst(x + y * wwidth); - Fx.lightBlock.at(x * tilesize, y * tilesize, 1f, Color.orange); + for(int i = -1; i < result.size; i++){ + int + current = i == -1 ? node : result.items[i], + cluster = NodeIndex.cluster(current), + dir = NodeIndex.dir(current), + dx = Geometry.d4[dir].x, + dy = Geometry.d4[dir].y, + ox = cluster % cwidth + dx, + oy = cluster / cwidth + dy; + + //store current cluster in the path list + if(!fields.containsKey(cluster)){ + fields.put(cluster, new int[fsize]); } - } - } - //TODO spread this out across many frames - while(frontier.size > 0){ - int tile = frontier.removeLast(); - int baseX = tile % wwidth, baseY = tile / wwidth; - int cost = weights[(baseX - minX) + (baseY - minY) * realSize]; + //store directionals TODO out of bounds + for(Point2 p : Geometry.d4){ + int other = cluster + p.x + p.y * cwidth; + if(!fields.containsKey(other)){ + fields.put(other, new int[fsize]); + } + } - if(cost != impassable){ - for(Point2 point : Geometry.d4){ + //store directional/flipped version of cluster + if(ox >= 0 && oy >= 0 && ox < cwidth && oy < cheight){ + int other = ox + oy * cwidth; + if(!fields.containsKey(other)){ + fields.put(other, new int[fsize]); + } - int dx = baseX + point.x, dy = baseY + point.y; - - if(dx < minX || dy < minY || dx > maxX || dy > maxY) continue; - - int newPos = tile + point.x + point.y * wwidth; - int newPosArray = (dx - minX) + (dy - minY) * realSize; - int otherCost = pcost.getCost(team, pathfinder.tiles[newPos]); - - if((weights[newPosArray] > cost + otherCost || searches[newPosArray] < search) && otherCost != impassable){ - frontier.addFirst(newPos); - weights[newPosArray] = cost + otherCost; - searches[newPosArray] = (byte)search; + //store directionals again + for(Point2 p : Geometry.d4){ + int other2 = other + p.x + p.y * cwidth; + if(!fields.containsKey(other2)){ + fields.put(other2, new int[fsize]); + } } } } - } - return weights; - } - public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 out, boolean[] noResultFound){ - int cost = 0; - - int node = findClosestNode(unit.team.id, cost, unit.tileX(), unit.tileY()); - int dest = findClosestNode(unit.team.id, cost, World.toTile(destination.x), World.toTile(destination.y)); - - var result = clusterAstar(cost, node, dest); - Tile tile = unit.tileOn(); - if(result != null){ for(int i = -1; i < result.size - 1; i++){ int current = i == -1 ? node : result.items[i], next = result.items[i + 1]; + portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), Tmp.v1); portalToVec(0, NodeIndex.cluster(next), NodeIndex.dir(next), NodeIndex.portal(next), Tmp.v2); line(Tmp.v1, Tmp.v2, Color.orange); } - int cx = unit.tileX() / clusterSize, cy = unit.tileY() / clusterSize, - ox = cx * clusterSize - 1, oy = cy * clusterSize - 1; + //actually do the flow field part + //TODO spread this out across many frames + while(frontier.size > 0){ + int tile = frontier.removeLast(); + int baseX = tile % wwidth, baseY = tile / wwidth; + int curWeightIndex = (baseX / clusterSize) + (baseY / clusterSize) * cwidth; + int[] curWeights = fields.get(curWeightIndex); - int nextNode = result.items[0]; + int cost = curWeights[baseX % clusterSize + ((baseY % clusterSize) * clusterSize)]; - int[] field = flowField(cost, cx, cy, node, nextNode, World.toTile(destination.x), World.toTile(destination.y)); + if(cost != impassable){ + for(Point2 point : Geometry.d4){ - if(field != null && tile != null){ - int value = field[(tile.x - ox) + (tile.y - oy) * (clusterSize + 2)]; + int + dx = baseX + point.x, dy = baseY + point.y, + clx = dx / clusterSize, cly = dy / clusterSize; - Tile current = null; - int tl = 0; - for(Point2 point : Geometry.d8){ - int dx = tile.x + point.x, dy = tile.y + point.y; + if(clx < 0 || cly < 0 || dx >= wwidth || dy >= wheight) continue; - Tile other = world.tile(dx, dy); + int nextWeightIndex = clx + cly * cwidth; + int[] weights = nextWeightIndex == curWeightIndex ? curWeights : fields.get(nextWeightIndex); - if(other == null || dx < ox || dy < oy || dx >= ox + clusterSize + 2 || dy >= oy + clusterSize + 2) continue; + //out of bounds; not allowed to move this way because no weights were registered here + if(weights == null) continue; - int local = (dx - ox) + (dy - oy) * (clusterSize + 2); - int packed = world.packArray(dx, dy); - int otherCost = field[local]; + int newPos = tile + point.x + point.y * wwidth; - if(otherCost < value && (current == null || otherCost < tl) && passable(ControlPathfinder.costGround, unit.team.id, packed) && - !(point.x != 0 && point.y != 0 && (!passable(ControlPathfinder.costGround, unit.team.id, world.packArray(tile.x + point.x, tile.y)) || - (!passable(ControlPathfinder.costGround, unit.team.id, world.packArray(tile.x, tile.y + point.y)))))){ //diagonal corner trap + //can't move back to the goal + if(newPos == goalPos) continue; - current = other; - tl = field[local]; + int newPosArray = (dx - clx * clusterSize) + (dy - cly * clusterSize) * clusterSize; + int otherCost = pcost.getCost(team, pathfinder.tiles[newPos]); + int oldCost = weights[newPosArray]; + + //a cost of 0 means uninitialized, OR it means we're at the goal position, but that's handled above + if((oldCost == 0 || oldCost > cost + otherCost) && otherCost != impassable){ + frontier.addFirst(newPos); + weights[newPosArray] = cost + otherCost; + } } } + } - if(!(current == null || tl == impassable || (cost == costGround && current.dangerous() && !tile.dangerous()))){ - out.set(current); - return true; + + int value = getCost(fields, tileOn.x, tileOn.y); + + Tile current = null; + int tl = 0; + //TODO: use raycasting and iterate on this for N steps + for(Point2 point : Geometry.d8){ + int dx = tileOn.x + point.x, dy = tileOn.y + point.y; + + Tile other = world.tile(dx, dy); + + if(other == null) continue; + + int packed = world.packArray(dx, dy); + int otherCost = getCost(fields, dx, dy); + + if(otherCost < value && (current == null || otherCost < tl) && passable(ControlPathfinder.costGround, unit.team.id, packed) && + !(point.x != 0 && point.y != 0 && (!passable(ControlPathfinder.costGround, unit.team.id, world.packArray(tileOn.x + point.x, tileOn.y)) || + (!passable(ControlPathfinder.costGround, unit.team.id, world.packArray(tileOn.x, tileOn.y + point.y)))))){ //diagonal corner trap + + current = other; + tl = otherCost; } } + + if(!(current == null || tl == impassable || (costId == costGround && current.dangerous() && !tileOn.dangerous()))){ + out.set(current); + return true; + } } noResultFound[0] = true; return false; } + private int getCost(IntMap fields, int x, int y){ + int[] field = fields.get(x / clusterSize + (y / clusterSize) * cwidth); + if(field == null){ + return -1; + } + return field[(x % clusterSize) + (y % clusterSize) * clusterSize]; + } + private static boolean passable(PathCost cost, int team, int pos){ int amount = cost.getCost(team, pathfinder.tiles[pos]); //edge case: naval reports costs of 6000+ for non-liquids, even though they are not technically passable diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index 953274f8a1..246eec104a 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -2596,5 +2596,15 @@ public class Fx{ } Draw.reset(); + }), + debugRect = new Effect(90f, 1000000000000f, e -> { + if(!(e.data instanceof Rect rect)) return; + + Draw.color(e.color); + Lines.stroke(2f); + + Lines.rect(rect); + + Draw.reset(); }); } From 70e112e8849f6246d25a5d6181b7de0f5cfac8ba Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 8 Nov 2023 13:20:07 -0500 Subject: [PATCH 010/348] Moving new pathfinding to a new thread --- .../src/mindustry/ai/HierarchyPathFinder.java | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 8f0b500c99..97587f7ceb 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -23,6 +23,8 @@ import static mindustry.ai.Pathfinder.*; //https://webdocs.cs.ualberta.ca/~mmueller/ps/hpastar.pdf //https://www.gameaipro.com/GameAIPro/GameAIPro_Chapter23_Crowd_Pathfinding_and_Steering_Using_Flow_Field_Tiles.pdf public class HierarchyPathFinder{ + static final int clusterSize = 12; + static final boolean debug = true; static final int[] offsets = { @@ -48,7 +50,6 @@ public class HierarchyPathFinder{ //maps pathCost -> flattened array of clusters in 2D Cluster[][] clusters; - int clusterSize = 12; int cwidth, cheight; @@ -56,17 +57,45 @@ public class HierarchyPathFinder{ PathfindQueue frontier = new PathfindQueue(); //node index -> total cost IntFloatMap costs = new IntFloatMap(); + //temporarily used for resolving connections for intra-edges IntSet usedEdges = new IntSet(); //node index (NodeIndex struct) -> node it came from IntIntMap cameFrom = new IntIntMap(); IntMap fields; + //tasks to run on pathfinding thread + TaskQueue tasks = new TaskQueue(); + //individual requests based on unit + ObjectMap unitRequests = new ObjectMap<>(); + //maps position in world in (x + y * width format) to a cache of flow fields + IntMap requests = new IntMap<>(); + + //path requests are per-unit + //these contain + static class PathRequest{ + int destination; + //node index -> total cost + IntFloatMap costs = new IntFloatMap(); + //node index (NodeIndex struct) -> node it came from TODO merge them + IntIntMap cameFrom = new IntIntMap(); + } + + static class FieldCache{ + int destination; + //frontier for flow fields + PathfindQueue frontier = new PathfindQueue(); + //maps cluster index to field weights; 0 means uninitialized + IntMap fields = new IntMap<>(); + + //TODO: node map for merging + //TODO: how to extend flowfields? + } + public HierarchyPathFinder(){ Events.on(WorldLoadEvent.class, event -> { //TODO 5 path costs, arbitrary number clusters = new Cluster[5][]; - clusterSize = 12; //TODO arbitrary cwidth = Mathf.ceil((float)world.width() / clusterSize); cheight = Mathf.ceil((float)world.height() / clusterSize); From 358a9ca98bcf292a3845e06128ab1d92dcdfb935 Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 9 Nov 2023 09:49:50 -0500 Subject: [PATCH 011/348] progress --- .../src/mindustry/ai/HierarchyPathFinder.java | 138 +++++++++++------- 1 file changed, 82 insertions(+), 56 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 97587f7ceb..c2bcb34afa 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -22,7 +22,11 @@ import static mindustry.ai.Pathfinder.*; //https://webdocs.cs.ualberta.ca/~mmueller/ps/hpastar.pdf //https://www.gameaipro.com/GameAIPro/GameAIPro_Chapter23_Crowd_Pathfinding_and_Steering_Using_Flow_Field_Tiles.pdf -public class HierarchyPathFinder{ +public class HierarchyPathFinder implements Runnable{ + private static final long maxUpdate = Time.millisToNanos(12); + private static final int updateFPS = 30; + private static final int updateInterval = 1000 / updateFPS; + static final int clusterSize = 12; static final boolean debug = true; @@ -53,37 +57,35 @@ public class HierarchyPathFinder{ int cwidth, cheight; - //TODO: make thread-local (they are dereferenced rarely anyway) - PathfindQueue frontier = new PathfindQueue(); - //node index -> total cost - IntFloatMap costs = new IntFloatMap(); //temporarily used for resolving connections for intra-edges IntSet usedEdges = new IntSet(); - //node index (NodeIndex struct) -> node it came from - IntIntMap cameFrom = new IntIntMap(); - IntMap fields; - //tasks to run on pathfinding thread - TaskQueue tasks = new TaskQueue(); + TaskQueue queue = new TaskQueue(); //individual requests based on unit ObjectMap unitRequests = new ObjectMap<>(); //maps position in world in (x + y * width format) to a cache of flow fields IntMap requests = new IntMap<>(); + /** Current pathfinding thread */ + @Nullable Thread thread; //path requests are per-unit //these contain static class PathRequest{ int destination; + //resulting path of nodes + IntSeq resultPath = new IntSeq(); //node index -> total cost IntFloatMap costs = new IntFloatMap(); //node index (NodeIndex struct) -> node it came from TODO merge them IntIntMap cameFrom = new IntIntMap(); + //frontier for A* + PathfindQueue frontier = new PathfindQueue(); } static class FieldCache{ int destination; //frontier for flow fields - PathfindQueue frontier = new PathfindQueue(); + IntQueue frontier = new IntQueue(); //maps cluster index to field weights; 0 means uninitialized IntMap fields = new IntMap<>(); @@ -93,17 +95,17 @@ public class HierarchyPathFinder{ public HierarchyPathFinder(){ + Events.on(ResetEvent.class, event -> stop()); + Events.on(WorldLoadEvent.class, event -> { + stop(); + //TODO 5 path costs, arbitrary number clusters = new Cluster[5][]; cwidth = Mathf.ceil((float)world.width() / clusterSize); cheight = Mathf.ceil((float)world.height() / clusterSize); - for(int cy = 0; cy < cwidth; cy++){ - for(int cx = 0; cx < cheight; cx++){ - createCluster(Team.sharded.id, costGround, cx, cy); - } - } + start(); }); //TODO very inefficient, this is only for debugging @@ -183,33 +185,7 @@ public class HierarchyPathFinder{ } } - if(false){ - Lines.stroke(3f); - Draw.color(Color.orange); - int node = findClosestNode(Team.sharded.id, 0, player.tileX(), player.tileY()); - int dest = findClosestNode(Team.sharded.id, 0, World.toTile(Core.input.mouseWorldX()), World.toTile(Core.input.mouseWorldY())); - if(node != Integer.MAX_VALUE && dest != Integer.MAX_VALUE){ - var result = clusterAstar(0, node, dest); - if(result != null){ - - Lines.stroke(3f); - Draw.color(Color.orange); - - for(int i = -1; i < result.size - 1; i++){ - int current = i == -1 ? node : result.items[i], next = result.items[i + 1]; - portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), Tmp.v1); - portalToVec(0, NodeIndex.cluster(next), NodeIndex.dir(next), NodeIndex.portal(next), Tmp.v2); - Lines.line(Tmp.v1.x, Tmp.v1.y, Tmp.v2.x, Tmp.v2.y); - } - } - - nodeToVec(dest, Tmp.v1); - Fonts.outline.draw(clusterNodeHeuristic(0, node, dest) + "", Tmp.v1.x, Tmp.v1.y); - } - - Draw.reset(); - } - + /* if(fields != null){ for(var entry : fields){ int cx = entry.key % cwidth, cy = entry.key / cwidth; @@ -224,6 +200,7 @@ public class HierarchyPathFinder{ } } } + */ }); @@ -231,6 +208,50 @@ public class HierarchyPathFinder{ } } + /** Starts or restarts the pathfinding thread. */ + private void start(){ + stop(); + if(net.client()) return; + + thread = new Thread(this, "Control Pathfinder"); + thread.setPriority(Thread.MIN_PRIORITY); + thread.setDaemon(true); + thread.start(); + } + + /** Stops the pathfinding thread. */ + private void stop(){ + if(thread != null){ + thread.interrupt(); + thread = null; + } + queue.clear(); + } + + @Override + public void run(){ + while(true){ + if(net.client()) return; + try{ + + if(state.isPlaying()){ + queue.run(); + + //TODO: update everything + } + + try{ + Thread.sleep(updateInterval); + }catch(InterruptedException e){ + //stop looping when interrupted externally + return; + } + }catch(Throwable e){ + e.printStackTrace(); + } + } + } + Vec2 nodeToVec(int current, Vec2 out){ portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), out); return out; @@ -581,15 +602,20 @@ public class HierarchyPathFinder{ return Math.abs(x1 - x2) + Math.abs(y1 - y2); } - @Nullable IntSeq clusterAstar(int pathCost, int startNodeIndex, int endNodeIndex){ - var v1 = nodeToVec(startNodeIndex, Tmp.v1); - var v2 = nodeToVec(endNodeIndex, Tmp.v2); + @Nullable IntSeq clusterAstar(PathRequest request, int pathCost, int startNodeIndex, int endNodeIndex){ + var result = request.resultPath; if(startNodeIndex == endNodeIndex){ + result.clear(); + result.add(startNodeIndex); //TODO alloc - return IntSeq.with(startNodeIndex); + return result; } + var costs = request.costs; + var cameFrom = request.cameFrom; + var frontier = request.frontier; + frontier.clear(); costs.clear(); cameFrom.clear(); @@ -615,7 +641,7 @@ public class HierarchyPathFinder{ //edges for the cluster the node is 'in' if(innerCons != null){ - checkEdges(pathCost, current, endNodeIndex, cx, cy, innerCons); + checkEdges(request, pathCost, current, endNodeIndex, cx, cy, innerCons); } //edges that this node 'faces' from the other side @@ -626,13 +652,13 @@ public class HierarchyPathFinder{ int relativeDir = (dir + 2) % 4; LongSeq outerCons = nextCluster.portalConnections[relativeDir] == null ? null : nextCluster.portalConnections[relativeDir][portal]; if(outerCons != null){ - checkEdges(pathCost, current, endNodeIndex, nextCx, nextCy, outerCons); + checkEdges(request, pathCost, current, endNodeIndex, nextCx, nextCy, outerCons); } } } if(foundEnd){ - IntSeq result = new IntSeq(); + result.clear(); int cur = endNodeIndex; while(cur != startNodeIndex){ @@ -655,20 +681,20 @@ public class HierarchyPathFinder{ Fx.debugLine.at(a.x, a.y, 0f, color, new Vec2[]{a.cpy(), b.cpy()}); } - void checkEdges(int pathCost, int current, int goal, int cx, int cy, LongSeq connections){ + void checkEdges(PathRequest request, int pathCost, int current, int goal, int cx, int cy, LongSeq connections){ for(int i = 0; i < connections.size; i++){ long con = connections.items[i]; float cost = IntraEdge.cost(con); int otherDir = IntraEdge.dir(con), otherPortal = IntraEdge.portal(con); int next = makeNodeIndex(cx, cy, otherDir, otherPortal); - float newCost = costs.get(current) + cost; + float newCost = request.costs.get(current) + cost; - if(newCost < costs.get(next, Float.POSITIVE_INFINITY)){ - costs.put(next, newCost); + if(newCost < request.costs.get(next, Float.POSITIVE_INFINITY)){ + request.costs.put(next, newCost); - frontier.add(next, newCost + clusterNodeHeuristic(pathCost, next, goal)); - cameFrom.put(next, current); + request.frontier.add(next, newCost + clusterNodeHeuristic(pathCost, next, goal)); + request.cameFrom.put(next, current); //TODO debug line(nodeToVec(current, Tmp.v1), nodeToVec(next, Tmp.v2)); From 69e2f6b93b4592bf898af6e6461c98fb15526b59 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 10 Nov 2023 01:57:04 -0500 Subject: [PATCH 012/348] progress --- .../src/mindustry/ai/HierarchyPathFinder.java | 234 +++++++++++------- 1 file changed, 143 insertions(+), 91 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index c2bcb34afa..9c5e27ae1a 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -65,6 +65,12 @@ public class HierarchyPathFinder implements Runnable{ ObjectMap unitRequests = new ObjectMap<>(); //maps position in world in (x + y * width format) to a cache of flow fields IntMap requests = new IntMap<>(); + + + //these are for inner edge A* + IntFloatMap innerCosts = new IntFloatMap(); + PathfindQueue innerFrontier = new PathfindQueue(); + /** Current pathfinding thread */ @Nullable Thread thread; @@ -80,10 +86,16 @@ public class HierarchyPathFinder implements Runnable{ IntIntMap cameFrom = new IntIntMap(); //frontier for A* PathfindQueue frontier = new PathfindQueue(); + + public PathRequest(int destination){ + this.destination = destination; + } } static class FieldCache{ - int destination; + PathCost cost; + int team; + int goalPos; //frontier for flow fields IntQueue frontier = new IntQueue(); //maps cluster index to field weights; 0 means uninitialized @@ -91,6 +103,13 @@ public class HierarchyPathFinder implements Runnable{ //TODO: node map for merging //TODO: how to extend flowfields? + + + public FieldCache(PathCost cost, int team, int goalPos){ + this.cost = cost; + this.team = team; + this.goalPos = goalPos; + } } public HierarchyPathFinder(){ @@ -228,30 +247,6 @@ public class HierarchyPathFinder implements Runnable{ queue.clear(); } - @Override - public void run(){ - while(true){ - if(net.client()) return; - try{ - - if(state.isPlaying()){ - queue.run(); - - //TODO: update everything - } - - try{ - Thread.sleep(updateInterval); - }catch(InterruptedException e){ - //stop looping when interrupted externally - return; - } - }catch(Throwable e){ - e.printStackTrace(); - } - } - } - Vec2 nodeToVec(int current, Vec2 out){ portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), out); return out; @@ -451,6 +446,9 @@ public class HierarchyPathFinder implements Runnable{ /** @return -1 if no path was found */ float innerAstar(int team, PathCost cost, int minX, int minY, int maxX, int maxY, int startPos, int goalPos, int goalX1, int goalY1, int goalX2, int goalY2){ + var frontier = innerFrontier; + var costs = innerCosts; + frontier.clear(); costs.clear(); @@ -702,34 +700,103 @@ public class HierarchyPathFinder implements Runnable{ } } - public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 out, boolean[] noResultFound){ + private void updateFields(FieldCache cache, long nsToRun){ + var frontier = cache.frontier; + var fields = cache.fields; + var goalPos = cache.goalPos; + var pcost = cache.cost; + var team = cache.team; + + long start = Time.nanos(); + int counter = 0; + + //actually do the flow field part + //TODO spread this out across many frames + while(frontier.size > 0){ + int tile = frontier.removeLast(); + int baseX = tile % wwidth, baseY = tile / wwidth; + int curWeightIndex = (baseX / clusterSize) + (baseY / clusterSize) * cwidth; + int[] curWeights = fields.get(curWeightIndex); + + int cost = curWeights[baseX % clusterSize + ((baseY % clusterSize) * clusterSize)]; + + if(cost != impassable){ + for(Point2 point : Geometry.d4){ + + int + dx = baseX + point.x, dy = baseY + point.y, + clx = dx / clusterSize, cly = dy / clusterSize; + + if(clx < 0 || cly < 0 || dx >= wwidth || dy >= wheight) continue; + + int nextWeightIndex = clx + cly * cwidth; + + int[] weights = nextWeightIndex == curWeightIndex ? curWeights : fields.get(nextWeightIndex); + + //out of bounds; not allowed to move this way because no weights were registered here + if(weights == null) continue; + + int newPos = tile + point.x + point.y * wwidth; + + //can't move back to the goal + if(newPos == goalPos) continue; + + int newPosArray = (dx - clx * clusterSize) + (dy - cly * clusterSize) * clusterSize; + int otherCost = pcost.getCost(team, pathfinder.tiles[newPos]); + int oldCost = weights[newPosArray]; + + //a cost of 0 means uninitialized, OR it means we're at the goal position, but that's handled above + if((oldCost == 0 || oldCost > cost + otherCost) && otherCost != impassable){ + frontier.addFirst(newPos); + weights[newPosArray] = cost + otherCost; + } + } + } + + //every N iterations, check the time spent - this prevents extra calls to nano time, which itself is slow + if(nsToRun >= 0 && (counter++) >= 200){ + counter = 0; + if(Time.timeSinceNanos(start) >= nsToRun){ + return; + } + } + } + } + + public void createPathRequest(Unit unit, int goalX, int goalY){ int costId = 0; - - int node = findClosestNode(unit.team.id, costId, unit.tileX(), unit.tileY()); - int dest = findClosestNode(unit.team.id, costId, World.toTile(destination.x), World.toTile(destination.y)); - - fields = new IntMap<>(); - PathCost pcost = ControlPathfinder.costGround; - int team = Team.sharded.id; - int goalPos = (World.toTile(destination.x) + World.toTile(destination.y) * wwidth); + int team = unit.team.id; - IntQueue frontier = new IntQueue(); - frontier.addFirst(goalPos); + int goalPos = (goalX + goalY * wwidth); - Tile tileOn = unit.tileOn(); + int node = findClosestNode(team, costId, unit.tileX(), unit.tileY()); + int dest = findClosestNode(team, costId, goalX, goalY); - var result = clusterAstar(costId, node, dest); - if(result != null && tileOn != null){ + //TODO: not new? + PathRequest request = new PathRequest(dest); + + var nodePath = clusterAstar(request, costId, node, dest); + + //TODO: how to reuse + FieldCache cache = this.requests.get(goalPos, () -> new FieldCache(pcost, team, goalPos)); + + if(cache.frontier.isEmpty()){ + cache.frontier.addFirst(goalPos); + } + + if(nodePath != null){ int fsize = clusterSize * clusterSize; int cx = unit.tileX() / clusterSize, cy = unit.tileY() / clusterSize; + var fields = cache.fields; + fields.put(cx + cy * cwidth, new int[fsize]); - for(int i = -1; i < result.size; i++){ + for(int i = -1; i < nodePath.size; i++){ int - current = i == -1 ? node : result.items[i], + current = i == -1 ? node : nodePath.items[i], cluster = NodeIndex.cluster(current), dir = NodeIndex.dir(current), dx = Geometry.d4[dir].x, @@ -766,60 +833,16 @@ public class HierarchyPathFinder implements Runnable{ } } } + } - for(int i = -1; i < result.size - 1; i++){ - int current = i == -1 ? node : result.items[i], next = result.items[i + 1]; + } - portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), Tmp.v1); - portalToVec(0, NodeIndex.cluster(next), NodeIndex.dir(next), NodeIndex.portal(next), Tmp.v2); - line(Tmp.v1, Tmp.v2, Color.orange); - } - - //actually do the flow field part - //TODO spread this out across many frames - while(frontier.size > 0){ - int tile = frontier.removeLast(); - int baseX = tile % wwidth, baseY = tile / wwidth; - int curWeightIndex = (baseX / clusterSize) + (baseY / clusterSize) * cwidth; - int[] curWeights = fields.get(curWeightIndex); - - int cost = curWeights[baseX % clusterSize + ((baseY % clusterSize) * clusterSize)]; - - if(cost != impassable){ - for(Point2 point : Geometry.d4){ - - int - dx = baseX + point.x, dy = baseY + point.y, - clx = dx / clusterSize, cly = dy / clusterSize; - - if(clx < 0 || cly < 0 || dx >= wwidth || dy >= wheight) continue; - - int nextWeightIndex = clx + cly * cwidth; - - int[] weights = nextWeightIndex == curWeightIndex ? curWeights : fields.get(nextWeightIndex); - - //out of bounds; not allowed to move this way because no weights were registered here - if(weights == null) continue; - - int newPos = tile + point.x + point.y * wwidth; - - //can't move back to the goal - if(newPos == goalPos) continue; - - int newPosArray = (dx - clx * clusterSize) + (dy - cly * clusterSize) * clusterSize; - int otherCost = pcost.getCost(team, pathfinder.tiles[newPos]); - int oldCost = weights[newPosArray]; - - //a cost of 0 means uninitialized, OR it means we're at the goal position, but that's handled above - if((oldCost == 0 || oldCost > cost + otherCost) && otherCost != impassable){ - frontier.addFirst(newPos); - weights[newPosArray] = cost + otherCost; - } - } - } - } + public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 out, boolean[] noResultFound){ + int costId = 0; + Tile tileOn = unit.tileOn(); + if(tileOn != null){ int value = getCost(fields, tileOn.x, tileOn.y); Tile current = null; @@ -887,6 +910,35 @@ public class HierarchyPathFinder implements Runnable{ return cost.getCost(team, pathfinder.tiles[tilePos]); } + @Override + public void run(){ + while(true){ + if(net.client()) return; + try{ + + if(state.isPlaying()){ + queue.run(); + + //TODO: update everything else too + + //each update time (not total!) no longer than maxUpdate + for(FieldCache cache : requests.values()){ + updateFields(cache, maxUpdate); + } + } + + try{ + Thread.sleep(updateInterval); + }catch(InterruptedException e){ + //stop looping when interrupted externally + return; + } + }catch(Throwable e){ + e.printStackTrace(); + } + } + } + static class Cluster{ IntSeq[] portals = new IntSeq[4]; //maps rotation + index of portal to list of IntraEdge objects From 24b7d99a691b21162b84d0575665da71fce68f16 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 10 Nov 2023 13:48:41 -0500 Subject: [PATCH 013/348] progress --- .../src/mindustry/ai/HierarchyPathFinder.java | 161 +++++++++++++----- 1 file changed, 118 insertions(+), 43 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 9c5e27ae1a..0a3943be45 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -14,7 +14,6 @@ import mindustry.game.EventType.*; import mindustry.game.*; import mindustry.gen.*; import mindustry.graphics.*; -import mindustry.ui.*; import mindustry.world.*; import static mindustry.Vars.*; @@ -53,6 +52,7 @@ public class HierarchyPathFinder implements Runnable{ }; //maps pathCost -> flattened array of clusters in 2D + //(what about teams? different path costs?) Cluster[][] clusters; int cwidth, cheight; @@ -64,42 +64,51 @@ public class HierarchyPathFinder implements Runnable{ //individual requests based on unit ObjectMap unitRequests = new ObjectMap<>(); //maps position in world in (x + y * width format) to a cache of flow fields - IntMap requests = new IntMap<>(); + IntMap fields = new IntMap<>(); //these are for inner edge A* IntFloatMap innerCosts = new IntFloatMap(); PathfindQueue innerFrontier = new PathfindQueue(); + //ONLY modify on pathfinding thread. + IntSet clustersToUpdate = new IntSet(); + IntSet clustersToInnerUpdate = new IntSet(); + /** Current pathfinding thread */ @Nullable Thread thread; //path requests are per-unit //these contain static class PathRequest{ - int destination; + final Unit unit; + final int destination; //resulting path of nodes - IntSeq resultPath = new IntSeq(); + final IntSeq resultPath = new IntSeq(); //node index -> total cost - IntFloatMap costs = new IntFloatMap(); + final IntFloatMap costs = new IntFloatMap(); //node index (NodeIndex struct) -> node it came from TODO merge them - IntIntMap cameFrom = new IntIntMap(); + final IntIntMap cameFrom = new IntIntMap(); //frontier for A* - PathfindQueue frontier = new PathfindQueue(); + final PathfindQueue frontier = new PathfindQueue(); - public PathRequest(int destination){ + //main thread only! + long lastUpdateId; + + public PathRequest(Unit unit, int destination){ + this.unit = unit; this.destination = destination; } } static class FieldCache{ - PathCost cost; - int team; - int goalPos; + final PathCost cost; + final int team; + final int goalPos; //frontier for flow fields - IntQueue frontier = new IntQueue(); + final IntQueue frontier = new IntQueue(); //maps cluster index to field weights; 0 means uninitialized - IntMap fields = new IntMap<>(); + final IntMap fields = new IntMap<>(); //TODO: node map for merging //TODO: how to extend flowfields? @@ -129,7 +138,35 @@ public class HierarchyPathFinder implements Runnable{ //TODO very inefficient, this is only for debugging Events.on(TileChangeEvent.class, e -> { - createCluster(Team.sharded.id, costGround, e.tile.x / clusterSize, e.tile.y / clusterSize); + + e.tile.getLinkedTiles(t -> { + int x = t.x, y = t.y, mx = x % clusterSize, my = y % clusterSize, cx = x / clusterSize, cy = y / clusterSize, cluster = cx + cy * cwidth; + + //is at the edge of a cluster; this means the portals may have changed. + if(mx == 0 || my == 0 || mx == clusterSize - 1 || my == clusterSize - 1){ + queue.post(() -> clustersToUpdate.add(cluster)); + }else{ + //there is no need to recompute portals for block updates that are not on the edge. + queue.post(() -> clustersToInnerUpdate.add(cluster)); + } + }); + + //TODO: if near center of cluster: + //- re-do inner A* only + //- otherwise, re-do everything + + //TODO: recalculate affected flow fields? or just all of them? + }); + + //invalidate paths + Events.run(Trigger.update, () -> { + for(var req : unitRequests.values()){ + //skipped N update -> drop it + if(req.lastUpdateId <= state.updateId - 10){ + //concurrent modification! + Core.app.post(() -> unitRequests.remove(req.unit)); + } + } }); if(debug){ @@ -269,6 +306,7 @@ public class HierarchyPathFinder implements Runnable{ out.set(x, y); } + //TODO: this is never called yet. should be invoked during pathfinding void createCluster(int team, int pathCost, int cx, int cy){ if(clusters[pathCost] == null) clusters[pathCost] = new Cluster[cwidth * cheight]; Cluster cluster = clusters[pathCost][cy * cwidth + cx]; @@ -524,6 +562,7 @@ public class HierarchyPathFinder implements Runnable{ //TODO PathCost cost = ControlPathfinder.costGround; + //TODO: cluster can be null!! Cluster cluster = clusters[pathCost][cx + cy * cwidth]; int minX = cx * clusterSize, minY = cy * clusterSize, maxX = Math.min(minX + clusterSize - 1, wwidth - 1), maxY = Math.min(minY + clusterSize - 1, wheight - 1); @@ -763,23 +802,19 @@ public class HierarchyPathFinder implements Runnable{ } } - public void createPathRequest(Unit unit, int goalX, int goalY){ + public void initializePathRequest(PathRequest request, int team, int unitX, int unitY, int goalX, int goalY){ int costId = 0; PathCost pcost = ControlPathfinder.costGround; - int team = unit.team.id; int goalPos = (goalX + goalY * wwidth); - int node = findClosestNode(team, costId, unit.tileX(), unit.tileY()); + int node = findClosestNode(team, costId, unitX, unitY); int dest = findClosestNode(team, costId, goalX, goalY); - //TODO: not new? - PathRequest request = new PathRequest(dest); - var nodePath = clusterAstar(request, costId, node, dest); //TODO: how to reuse - FieldCache cache = this.requests.get(goalPos, () -> new FieldCache(pcost, team, goalPos)); + FieldCache cache = this.fields.get(goalPos, () -> new FieldCache(pcost, team, goalPos)); if(cache.frontier.isEmpty()){ cache.frontier.addFirst(goalPos); @@ -788,7 +823,7 @@ public class HierarchyPathFinder implements Runnable{ if(nodePath != null){ int fsize = clusterSize * clusterSize; - int cx = unit.tileX() / clusterSize, cy = unit.tileY() / clusterSize; + int cx = unitX / clusterSize, cy = unitY / clusterSize; var fields = cache.fields; @@ -840,37 +875,63 @@ public class HierarchyPathFinder implements Runnable{ public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 out, boolean[] noResultFound){ int costId = 0; - Tile tileOn = unit.tileOn(); + PathRequest request = unitRequests.get(unit); + int + destX = World.toTile(destination.x), + destY = World.toTile(destination.y) * wwidth, + destPos = destX + destY * wwidth; - if(tileOn != null){ - int value = getCost(fields, tileOn.x, tileOn.y); + //TODO: collect old requests that have not been accessed in a while. not sure where. + request.lastUpdateId = state.updateId; - Tile current = null; - int tl = 0; - //TODO: use raycasting and iterate on this for N steps - for(Point2 point : Geometry.d8){ - int dx = tileOn.x + point.x, dy = tileOn.y + point.y; + //use existing request if it exists. + if(request != null && request.destination == destPos){ - Tile other = world.tile(dx, dy); + Tile tileOn = unit.tileOn(); + //TODO: should fields be accessible from this thread? + FieldCache fieldCache = fields.get(destPos); - if(other == null) continue; + if(tileOn != null && fieldCache != null){ + int value = getCost(fieldCache.fields, tileOn.x, tileOn.y); - int packed = world.packArray(dx, dy); - int otherCost = getCost(fields, dx, dy); + Tile current = null; + int tl = 0; + //TODO: use raycasting and iterate on this for N steps + for(Point2 point : Geometry.d8){ + int dx = tileOn.x + point.x, dy = tileOn.y + point.y; - if(otherCost < value && (current == null || otherCost < tl) && passable(ControlPathfinder.costGround, unit.team.id, packed) && - !(point.x != 0 && point.y != 0 && (!passable(ControlPathfinder.costGround, unit.team.id, world.packArray(tileOn.x + point.x, tileOn.y)) || + Tile other = world.tile(dx, dy); + + if(other == null) continue; + + int packed = world.packArray(dx, dy); + int otherCost = getCost(fieldCache.fields, dx, dy); + + if(otherCost < value && (current == null || otherCost < tl) && passable(ControlPathfinder.costGround, unit.team.id, packed) && + !(point.x != 0 && point.y != 0 && (!passable(ControlPathfinder.costGround, unit.team.id, world.packArray(tileOn.x + point.x, tileOn.y)) || (!passable(ControlPathfinder.costGround, unit.team.id, world.packArray(tileOn.x, tileOn.y + point.y)))))){ //diagonal corner trap - current = other; - tl = otherCost; + current = other; + tl = otherCost; + } + } + + if(!(current == null || tl == impassable || (costId == costGround && current.dangerous() && !tileOn.dangerous()))){ + out.set(current); + return true; } } - if(!(current == null || tl == impassable || (costId == costGround && current.dangerous() && !tileOn.dangerous()))){ - out.set(current); - return true; - } + }else{ + //queue new request. + unitRequests.put(unit, request = new PathRequest(unit, destPos)); + + PathRequest f = request; + + //on the pathfinding thread: initialize the request, meaning + queue.post(() -> { + initializePathRequest(f, unit.team.id, unit.tileX(), unit.tileY(), destX, destY); + }); } noResultFound[0] = true; @@ -919,10 +980,24 @@ public class HierarchyPathFinder implements Runnable{ if(state.isPlaying()){ queue.run(); + clustersToUpdate.each(cluster -> { + + //just in case: don't redundantly update inner clusters after you've recalculated it entirely + clustersToInnerUpdate.remove(cluster); + }); + + clustersToInnerUpdate.each(cluster -> { + + //only recompute the inner links + }); + + clustersToInnerUpdate.clear(); + clustersToUpdate.clear(); + //TODO: update everything else too //each update time (not total!) no longer than maxUpdate - for(FieldCache cache : requests.values()){ + for(FieldCache cache : fields.values()){ updateFields(cache, maxUpdate); } } From 5a14302d68fe1623e9d0212ccf6f2124240e15ff Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 11 Nov 2023 10:41:53 -0500 Subject: [PATCH 014/348] progress --- core/src/mindustry/ai/ControlPathfinder.java | 13 ++ .../src/mindustry/ai/HierarchyPathFinder.java | 153 +++++++++++++----- 2 files changed, 122 insertions(+), 44 deletions(-) diff --git a/core/src/mindustry/ai/ControlPathfinder.java b/core/src/mindustry/ai/ControlPathfinder.java index 98033a4da2..f6b166270e 100644 --- a/core/src/mindustry/ai/ControlPathfinder.java +++ b/core/src/mindustry/ai/ControlPathfinder.java @@ -62,6 +62,19 @@ public class ControlPathfinder{ ((PathTile.team(tile) != team && PathTile.team(tile) != 0) && PathTile.solid(tile) ? wallImpassableCap : 0) + (PathTile.nearGround(tile) || PathTile.nearSolid(tile) ? 6 : 0); + public static final int + costIdGround = 0, + costIdHover = 1, + costIdLegs = 2, + costIdNaval = 3; + + public static final Seq costTypes = Seq.with( + costGround, + costHover, + costLegs, + costNaval + ); + public static boolean showDebug = false; //static access probably faster than object access diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 0a3943be45..d4875637ba 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -51,9 +51,9 @@ public class HierarchyPathFinder implements Runnable{ 0, -1 }; - //maps pathCost -> flattened array of clusters in 2D + //maps team -> pathCost -> flattened array of clusters in 2D //(what about teams? different path costs?) - Cluster[][] clusters; + Cluster[][][] clusters; int cwidth, cheight; @@ -82,7 +82,7 @@ public class HierarchyPathFinder implements Runnable{ //these contain static class PathRequest{ final Unit unit; - final int destination; + final int destination, team; //resulting path of nodes final IntSeq resultPath = new IntSeq(); //node index -> total cost @@ -93,10 +93,11 @@ public class HierarchyPathFinder implements Runnable{ final PathfindQueue frontier = new PathfindQueue(); //main thread only! - long lastUpdateId; + long lastUpdateId = state.updateId; - public PathRequest(Unit unit, int destination){ + public PathRequest(Unit unit, int team, int destination){ this.unit = unit; + this.team = team; this.destination = destination; } } @@ -129,7 +130,7 @@ public class HierarchyPathFinder implements Runnable{ stop(); //TODO 5 path costs, arbitrary number - clusters = new Cluster[5][]; + clusters = new Cluster[256][][]; cwidth = Mathf.ceil((float)world.width() / clusterSize); cheight = Mathf.ceil((float)world.height() / clusterSize); @@ -144,7 +145,16 @@ public class HierarchyPathFinder implements Runnable{ //is at the edge of a cluster; this means the portals may have changed. if(mx == 0 || my == 0 || mx == clusterSize - 1 || my == clusterSize - 1){ - queue.post(() -> clustersToUpdate.add(cluster)); + + + if(mx == 0) queueClusterUpdate(cx - 1, cy); //left + if(my == 0) queueClusterUpdate(cx, cy - 1); //bottom + if(mx == clusterSize - 1) queueClusterUpdate(cx + 1, cy); //right + if(my == clusterSize - 1) queueClusterUpdate(cx, cy + 1); //top + + + queueClusterUpdate(cx, cy); + //TODO: recompute edge clusters too. }else{ //there is no need to recompute portals for block updates that are not on the edge. queue.post(() -> clustersToInnerUpdate.add(cluster)); @@ -169,7 +179,7 @@ public class HierarchyPathFinder implements Runnable{ } }); - if(debug){ + if(debug && false){ Events.run(Trigger.draw, () -> { int team = Team.sharded.id; int cost = costGround; @@ -180,7 +190,7 @@ public class HierarchyPathFinder implements Runnable{ Lines.stroke(1f); for(int cx = 0; cx < cwidth; cx++){ for(int cy = 0; cy < cheight; cy++){ - var cluster = clusters[cost][cy * cwidth + cx]; + var cluster = clusters[Team.sharded.id][cost][cy * cwidth + cx]; if(cluster != null){ Lines.stroke(0.5f); Draw.color(Color.gray); @@ -264,6 +274,35 @@ public class HierarchyPathFinder implements Runnable{ } } + void queueClusterUpdate(int cx, int cy){ + if(cx >= 0 && cy >= 0 && cx < cwidth && cy < cheight){ + queue.post(() -> clustersToUpdate.add(cx + cy * cwidth)); + } + } + + //DEBUGGING ONLY + Vec2 nodeToVec(int current, Vec2 out){ + portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), out); + return out; + } + + void portalToVec(int pathCost, int cluster, int direction, int portalIndex, Vec2 out){ + portalToVec(clusters[Team.sharded.id][pathCost][cluster], cluster % cwidth, cluster / cwidth, direction, portalIndex, out); + } + + void portalToVec(Cluster cluster, int cx, int cy, int direction, int portalIndex, Vec2 out){ + int pos = cluster.portals[direction].items[portalIndex]; + int from = Point2.x(pos), to = Point2.y(pos); + int addX = moveDirs[direction * 2], addY = moveDirs[direction * 2 + 1]; + float average = (from + to) / 2f; + + float + x = (addX * average + cx * clusterSize + offsets[direction * 2] * (clusterSize - 1) + nextOffsets[direction * 2] / 2f) * tilesize, + y = (addY * average + cy * clusterSize + offsets[direction * 2 + 1] * (clusterSize - 1) + nextOffsets[direction * 2 + 1] / 2f) * tilesize; + + out.set(x, y); + } + /** Starts or restarts the pathfinding thread. */ private void start(){ stop(); @@ -284,34 +323,56 @@ public class HierarchyPathFinder implements Runnable{ queue.clear(); } - Vec2 nodeToVec(int current, Vec2 out){ - portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), out); - return out; + /** @return a cluster at coordinates; can be null if not cluster was created yet*/ + @Nullable Cluster getCluster(int team, int pathCost, int cx, int cy){ + return getCluster(team, pathCost, cx + cy * cwidth); } - void portalToVec(int pathCost, int cluster, int direction, int portalIndex, Vec2 out){ - portalToVec(clusters[pathCost][cluster], cluster % cwidth, cluster / cwidth, direction, portalIndex, out); + /** @return a cluster at coordinates; can be null if not cluster was created yet*/ + @Nullable Cluster getCluster(int team, int pathCost, int clusterIndex){ + Cluster[][] dim1 = clusters[team]; + + if(dim1 == null) return null; + + Cluster[] dim2 = dim1[pathCost]; + + if(dim2 == null) return null; + + return dim2[clusterIndex]; } - void portalToVec(Cluster cluster, int cx, int cy, int direction, int portalIndex, Vec2 out){ - int pos = cluster.portals[direction].items[portalIndex]; - int from = Point2.x(pos), to = Point2.y(pos); - int addX = moveDirs[direction * 2], addY = moveDirs[direction * 2 + 1]; - float average = (from + to) / 2f; + /** @return the cluster at specified coordinates; never null. */ + Cluster getCreateCluster(int team, int pathCost, int cx, int cy){ + return getCreateCluster(team, pathCost, cx + cy * cwidth); + } - float - x = (addX * average + cx * clusterSize + offsets[direction * 2] * (clusterSize - 1) + nextOffsets[direction * 2] / 2f) * tilesize, - y = (addY * average + cy * clusterSize + offsets[direction * 2 + 1] * (clusterSize - 1) + nextOffsets[direction * 2 + 1] / 2f) * tilesize; - - out.set(x, y); + /** @return the cluster at specified coordinates; never null. */ + Cluster getCreateCluster(int team, int pathCost, int clusterIndex){ + Cluster result = getCluster(team, pathCost, clusterIndex); + if(result == null){ + return createCluster(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth); + }else{ + return result; + } } //TODO: this is never called yet. should be invoked during pathfinding - void createCluster(int team, int pathCost, int cx, int cy){ - if(clusters[pathCost] == null) clusters[pathCost] = new Cluster[cwidth * cheight]; - Cluster cluster = clusters[pathCost][cy * cwidth + cx]; + Cluster createCluster(int team, int pathCost, int cx, int cy){ + Cluster[][] dim1 = clusters[team]; + + if(dim1 == null){ + dim1 = clusters[team] = new Cluster[Team.all.length][]; + } + + Cluster[] dim2 = dim1[pathCost]; + + if(dim2 == null){ + dim2 = dim1[pathCost] = new Cluster[cwidth * cheight]; + } + + Cluster cluster = dim2[cy * cwidth + cx]; if(cluster == null){ - cluster = clusters[pathCost][cy * cwidth + cx] = new Cluster(); + cluster = dim2[cy * cwidth + cx] = new Cluster(); }else{ //reset data for(var p : cluster.portals){ @@ -334,7 +395,7 @@ public class HierarchyPathFinder implements Runnable{ continue; } - Cluster other = clusters[pathCost][otherX + otherY * cwidth]; + Cluster other = dim2[otherX + otherY * cwidth]; IntSeq portals; if(other == null){ @@ -388,6 +449,8 @@ public class HierarchyPathFinder implements Runnable{ } connectInnerEdges(cx, cy, team, cost, cluster); + + return cluster; } void connectInnerEdges(int cx, int cy, int team, PathCost cost, Cluster cluster){ @@ -563,7 +626,7 @@ public class HierarchyPathFinder implements Runnable{ PathCost cost = ControlPathfinder.costGround; //TODO: cluster can be null!! - Cluster cluster = clusters[pathCost][cx + cy * cwidth]; + Cluster cluster = getCreateCluster(team, pathCost, cx, cy); int minX = cx * clusterSize, minY = cy * clusterSize, maxX = Math.min(minX + clusterSize - 1, wwidth - 1), maxY = Math.min(minY + clusterSize - 1, wheight - 1); int bestPortalPair = Integer.MAX_VALUE; @@ -616,7 +679,7 @@ public class HierarchyPathFinder implements Runnable{ } //distance heuristic: manhattan - private float clusterNodeHeuristic(int pathCost, int nodeA, int nodeB){ + private float clusterNodeHeuristic(int team, int pathCost, int nodeA, int nodeB){ int clusterA = NodeIndex.cluster(nodeA), dirA = NodeIndex.dir(nodeA), @@ -624,8 +687,8 @@ public class HierarchyPathFinder implements Runnable{ clusterB = NodeIndex.cluster(nodeB), dirB = NodeIndex.dir(nodeB), portalB = NodeIndex.portal(nodeB), - rangeA = clusters[pathCost][clusterA].portals[dirA].items[portalA], - rangeB = clusters[pathCost][clusterB].portals[dirB].items[portalB]; + rangeA = getCreateCluster(team, pathCost, clusterA).portals[dirA].items[portalA], + rangeB = getCreateCluster(team, pathCost, clusterB).portals[dirB].items[portalB]; float averageA = (Point2.x(rangeA) + Point2.y(rangeA)) / 2f, @@ -652,6 +715,7 @@ public class HierarchyPathFinder implements Runnable{ var costs = request.costs; var cameFrom = request.cameFrom; var frontier = request.frontier; + var team = request.team; frontier.clear(); costs.clear(); @@ -673,23 +737,22 @@ public class HierarchyPathFinder implements Runnable{ int cluster = NodeIndex.cluster(current), dir = NodeIndex.dir(current), portal = NodeIndex.portal(current); int cx = cluster % cwidth, cy = cluster / cwidth; - Cluster clust = clusters[pathCost][cluster]; + Cluster clust = getCreateCluster(team, pathCost, cluster); LongSeq innerCons = clust.portalConnections[dir] == null || portal >= clust.portalConnections[dir].length ? null : clust.portalConnections[dir][portal]; //edges for the cluster the node is 'in' if(innerCons != null){ - checkEdges(request, pathCost, current, endNodeIndex, cx, cy, innerCons); + checkEdges(request, team, pathCost, current, endNodeIndex, cx, cy, innerCons); } //edges that this node 'faces' from the other side int nextCx = cx + Geometry.d4[dir].x, nextCy = cy + Geometry.d4[dir].y; if(nextCx >= 0 && nextCy >= 0 && nextCx < cwidth && nextCy < cheight){ - int nextClusteri = nextCx + nextCy * cwidth; - Cluster nextCluster = clusters[pathCost][nextClusteri]; + Cluster nextCluster = getCreateCluster(team, pathCost, nextCx, nextCy); int relativeDir = (dir + 2) % 4; LongSeq outerCons = nextCluster.portalConnections[relativeDir] == null ? null : nextCluster.portalConnections[relativeDir][portal]; if(outerCons != null){ - checkEdges(request, pathCost, current, endNodeIndex, nextCx, nextCy, outerCons); + checkEdges(request, team, pathCost, current, endNodeIndex, nextCx, nextCy, outerCons); } } } @@ -718,7 +781,7 @@ public class HierarchyPathFinder implements Runnable{ Fx.debugLine.at(a.x, a.y, 0f, color, new Vec2[]{a.cpy(), b.cpy()}); } - void checkEdges(PathRequest request, int pathCost, int current, int goal, int cx, int cy, LongSeq connections){ + void checkEdges(PathRequest request, int team, int pathCost, int current, int goal, int cx, int cy, LongSeq connections){ for(int i = 0; i < connections.size; i++){ long con = connections.items[i]; float cost = IntraEdge.cost(con); @@ -730,7 +793,7 @@ public class HierarchyPathFinder implements Runnable{ if(newCost < request.costs.get(next, Float.POSITIVE_INFINITY)){ request.costs.put(next, newCost); - request.frontier.add(next, newCost + clusterNodeHeuristic(pathCost, next, goal)); + request.frontier.add(next, newCost + clusterNodeHeuristic(team, pathCost, next, goal)); request.cameFrom.put(next, current); //TODO debug @@ -881,11 +944,10 @@ public class HierarchyPathFinder implements Runnable{ destY = World.toTile(destination.y) * wwidth, destPos = destX + destY * wwidth; - //TODO: collect old requests that have not been accessed in a while. not sure where. - request.lastUpdateId = state.updateId; - //use existing request if it exists. if(request != null && request.destination == destPos){ + //TODO: collect old requests that have not been accessed in a while. not sure where. + request.lastUpdateId = state.updateId; Tile tileOn = unit.tileOn(); //TODO: should fields be accessible from this thread? @@ -923,8 +985,9 @@ public class HierarchyPathFinder implements Runnable{ } }else{ + //queue new request. - unitRequests.put(unit, request = new PathRequest(unit, destPos)); + unitRequests.put(unit, request = new PathRequest(unit, unit.team.id, destPos)); PathRequest f = request; @@ -980,8 +1043,10 @@ public class HierarchyPathFinder implements Runnable{ if(state.isPlaying()){ queue.run(); + //TODO: WHICH clusters need to update here? do I iterate through 256 teams every time? ugh clustersToUpdate.each(cluster -> { + //just in case: don't redundantly update inner clusters after you've recalculated it entirely clustersToInnerUpdate.remove(cluster); }); From 0cf27034cff50205bc9bbdc41c922c2119adc391 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 11 Nov 2023 11:26:42 -0500 Subject: [PATCH 015/348] progress --- .../src/mindustry/ai/HierarchyPathFinder.java | 68 +++++++++++++------ 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index d4875637ba..38ce549794 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -114,7 +114,6 @@ public class HierarchyPathFinder implements Runnable{ //TODO: node map for merging //TODO: how to extend flowfields? - public FieldCache(PathCost cost, int team, int goalPos){ this.cost = cost; this.team = team; @@ -350,14 +349,13 @@ public class HierarchyPathFinder implements Runnable{ Cluster getCreateCluster(int team, int pathCost, int clusterIndex){ Cluster result = getCluster(team, pathCost, clusterIndex); if(result == null){ - return createCluster(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth); + return updateCluster(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth); }else{ return result; } } - //TODO: this is never called yet. should be invoked during pathfinding - Cluster createCluster(int team, int pathCost, int cx, int cy){ + Cluster updateCluster(int team, int pathCost, int cx, int cy){ Cluster[][] dim1 = clusters[team]; if(dim1 == null){ @@ -380,13 +378,7 @@ public class HierarchyPathFinder implements Runnable{ } } - //clear all connections, since portals changed, they need to be recomputed. - cluster.portalConnections = new LongSeq[4][]; - - //TODO: other cluster inner edges should be recomputed if changed. - - //TODO look it up based on number. - PathCost cost = ControlPathfinder.costGround; + PathCost cost = ControlPathfinder.costTypes.get(pathCost); for(int direction = 0; direction < 4; direction++){ int otherX = cx + Geometry.d4x(direction), otherY = cy + Geometry.d4y(direction); @@ -448,20 +440,22 @@ public class HierarchyPathFinder implements Runnable{ } } - connectInnerEdges(cx, cy, team, cost, cluster); + updateInnerEdges(team, cost, cx, cy, cluster); return cluster; } - void connectInnerEdges(int cx, int cy, int team, PathCost cost, Cluster cluster){ + void updateInnerEdges(int team, int cost, int cx, int cy, Cluster cluster){ + updateInnerEdges(team, ControlPathfinder.costTypes.get(cost), cx, cy, cluster); + } + + void updateInnerEdges(int team, PathCost cost, int cx, int cy, Cluster cluster){ int minX = cx * clusterSize, minY = cy * clusterSize, maxX = Math.min(minX + clusterSize - 1, wwidth - 1), maxY = Math.min(minY + clusterSize - 1, wheight - 1); usedEdges.clear(); - //TODO: how the hell to identify a vertex? - //cluster (i16) | direction (i2) | index (i14) - - //TODO: clear portal connections + //clear all connections, since portals changed, they need to be recomputed. + cluster.portalConnections = new LongSeq[4][]; for(int direction = 0; direction < 4; direction++){ var portals = cluster.portals[direction]; @@ -485,7 +479,6 @@ public class HierarchyPathFinder implements Runnable{ for(int j = 0; j < otherPortals.size; j++){ - //TODO redundant calculations? if(!usedEdges.contains(Point2.pack(otherDir, j))){ int @@ -1034,6 +1027,40 @@ public class HierarchyPathFinder implements Runnable{ return cost.getCost(team, pathfinder.tiles[tilePos]); } + private void updateClustersComplete(int clusterIndex){ + for(int team = 0; team < clusters.length; team++){ + var dim1 = clusters[team]; + if(dim1 != null){ + for(int pathCost = 0; pathCost < dim1.length; pathCost++){ + var dim2 = dim1[pathCost]; + if(dim2 != null){ + var cluster = dim2[clusterIndex]; + if(cluster != null){ + updateCluster(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth); + } + } + } + } + } + } + + private void updateClustersInner(int clusterIndex){ + for(int team = 0; team < clusters.length; team++){ + var dim1 = clusters[team]; + if(dim1 != null){ + for(int pathCost = 0; pathCost < dim1.length; pathCost++){ + var dim2 = dim1[pathCost]; + if(dim2 != null){ + var cluster = dim2[clusterIndex]; + if(cluster != null){ + updateInnerEdges(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth, cluster); + } + } + } + } + } + } + @Override public void run(){ while(true){ @@ -1043,17 +1070,16 @@ public class HierarchyPathFinder implements Runnable{ if(state.isPlaying()){ queue.run(); - //TODO: WHICH clusters need to update here? do I iterate through 256 teams every time? ugh clustersToUpdate.each(cluster -> { - + updateClustersComplete(cluster); //just in case: don't redundantly update inner clusters after you've recalculated it entirely clustersToInnerUpdate.remove(cluster); }); clustersToInnerUpdate.each(cluster -> { - //only recompute the inner links + updateClustersInner(cluster); }); clustersToInnerUpdate.clear(); From cacfe063625acff8b8a1be40dc54b940ad229c94 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 11 Nov 2023 12:28:10 -0500 Subject: [PATCH 016/348] progress --- .../src/mindustry/ai/HierarchyPathFinder.java | 250 +++++++++++------- 1 file changed, 148 insertions(+), 102 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 38ce549794..3732e4709b 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -61,8 +61,13 @@ public class HierarchyPathFinder implements Runnable{ IntSet usedEdges = new IntSet(); //tasks to run on pathfinding thread TaskQueue queue = new TaskQueue(); - //individual requests based on unit + + //individual requests based on unit - MAIN THREAD ONLY ObjectMap unitRequests = new ObjectMap<>(); + + //TODO: very dangerous usage; + //TODO - it is accessed from the main thread + //TODO - it is written to on the pathfinding thread //maps position in world in (x + y * width format) to a cache of flow fields IntMap fields = new IntMap<>(); @@ -79,7 +84,6 @@ public class HierarchyPathFinder implements Runnable{ @Nullable Thread thread; //path requests are per-unit - //these contain static class PathRequest{ final Unit unit; final int destination, team; @@ -111,8 +115,10 @@ public class HierarchyPathFinder implements Runnable{ //maps cluster index to field weights; 0 means uninitialized final IntMap fields = new IntMap<>(); - //TODO: node map for merging - //TODO: how to extend flowfields? + //main thread only! + long lastUpdateId = state.updateId; + + //TODO: how are the nodes merged? CAN they be merged? public FieldCache(PathCost cost, int team, int goalPos){ this.cost = cost; @@ -128,7 +134,7 @@ public class HierarchyPathFinder implements Runnable{ Events.on(WorldLoadEvent.class, event -> { stop(); - //TODO 5 path costs, arbitrary number + //TODO: can the pathfinding thread even see these? clusters = new Cluster[256][][]; cwidth = Mathf.ceil((float)world.width() / clusterSize); cheight = Mathf.ceil((float)world.height() / clusterSize); @@ -136,7 +142,6 @@ public class HierarchyPathFinder implements Runnable{ start(); }); - //TODO very inefficient, this is only for debugging Events.on(TileChangeEvent.class, e -> { e.tile.getLinkedTiles(t -> { @@ -160,10 +165,6 @@ public class HierarchyPathFinder implements Runnable{ } }); - //TODO: if near center of cluster: - //- re-do inner A* only - //- otherwise, re-do everything - //TODO: recalculate affected flow fields? or just all of them? }); @@ -176,9 +177,17 @@ public class HierarchyPathFinder implements Runnable{ Core.app.post(() -> unitRequests.remove(req.unit)); } } + + for(var field : fields.values()){ + //skipped N update -> drop it + if(field.lastUpdateId <= state.updateId - 20){ + //make sure it's only modified on the main thread...? but what about calling get() on this thread?? + queue.post(() -> fields.remove(field.goalPos)); + } + } }); - if(debug && false){ + if(debug){ Events.run(Trigger.draw, () -> { int team = Team.sharded.id; int cost = costGround; @@ -189,7 +198,9 @@ public class HierarchyPathFinder implements Runnable{ Lines.stroke(1f); for(int cx = 0; cx < cwidth; cx++){ for(int cy = 0; cy < cheight; cy++){ - var cluster = clusters[Team.sharded.id][cost][cy * cwidth + cx]; + if(clusters[Team.sharded.id] == null || clusters[team][cost] == null) continue; + + var cluster = clusters[team][cost][cy * cwidth + cx]; if(cluster != null){ Lines.stroke(0.5f); Draw.color(Color.gray); @@ -279,6 +290,14 @@ public class HierarchyPathFinder implements Runnable{ } } + static void line(Vec2 a, Vec2 b){ + Fx.debugLine.at(a.x, a.y, 0f, Color.blue.cpy().a(0.1f), new Vec2[]{a.cpy(), b.cpy()}); + } + + static void line(Vec2 a, Vec2 b, Color color){ + Fx.debugLine.at(a.x, a.y, 0f, color, new Vec2[]{a.cpy(), b.cpy()}); + } + //DEBUGGING ONLY Vec2 nodeToVec(int current, Vec2 out){ portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), out); @@ -329,6 +348,8 @@ public class HierarchyPathFinder implements Runnable{ /** @return a cluster at coordinates; can be null if not cluster was created yet*/ @Nullable Cluster getCluster(int team, int pathCost, int clusterIndex){ + if(clusters == null) return null; + Cluster[][] dim1 = clusters[team]; if(dim1 == null) return null; @@ -356,6 +377,8 @@ public class HierarchyPathFinder implements Runnable{ } Cluster updateCluster(int team, int pathCost, int cx, int cy){ + //TODO: what if clusters are null for thread visibility reasons? + Cluster[][] dim1 = clusters[team]; if(dim1 == null){ @@ -378,7 +401,7 @@ public class HierarchyPathFinder implements Runnable{ } } - PathCost cost = ControlPathfinder.costTypes.get(pathCost); + PathCost cost = idToCost(pathCost); for(int direction = 0; direction < 4; direction++){ int otherX = cx + Geometry.d4x(direction), otherY = cy + Geometry.d4y(direction); @@ -446,7 +469,7 @@ public class HierarchyPathFinder implements Runnable{ } void updateInnerEdges(int team, int cost, int cx, int cy, Cluster cluster){ - updateInnerEdges(team, ControlPathfinder.costTypes.get(cost), cx, cy, cluster); + updateInnerEdges(team, idToCost(cost), cx, cy, cluster); } void updateInnerEdges(int team, PathCost cost, int cx, int cy, Cluster cluster){ @@ -556,7 +579,7 @@ public class HierarchyPathFinder implements Runnable{ int cx = current % wwidth, cy = current / wwidth; //found the goal (it's in the portal rectangle) - //TODO portal rectangle approach does not work. + //TODO portal rectangle approach does not work, making this slower than it should be if((cx >= goalX1 && cy >= goalY1 && cx <= goalX2 && cy <= goalY2) || current == goalPos){ return costs.get(current); } @@ -615,59 +638,54 @@ public class HierarchyPathFinder implements Runnable{ return Integer.MAX_VALUE; } - //TODO - PathCost cost = ControlPathfinder.costGround; - - //TODO: cluster can be null!! + PathCost cost = idToCost(pathCost); Cluster cluster = getCreateCluster(team, pathCost, cx, cy); int minX = cx * clusterSize, minY = cy * clusterSize, maxX = Math.min(minX + clusterSize - 1, wwidth - 1), maxY = Math.min(minY + clusterSize - 1, wheight - 1); int bestPortalPair = Integer.MAX_VALUE; float bestCost = Float.MAX_VALUE; - if(cluster != null){ //TODO create on demand?? + //A* to every node, find the best one (I know there's a better algorithm for this, probably dijkstra) + for(int dir = 0; dir < 4; dir++){ + var portals = cluster.portals[dir]; + if(portals == null) continue; - //A* to every node, find the best one (I know there's a better algorithm for this, probably dijkstra) - for(int dir = 0; dir < 4; dir++){ - var portals = cluster.portals[dir]; - if(portals == null) continue; + for(int j = 0; j < portals.size; j++){ - for(int j = 0; j < portals.size; j++){ + int + other = portals.items[j], + otherFrom = Point2.x(other), otherTo = Point2.y(other), + otherAverage = (otherFrom + otherTo) / 2, + ox = cx * clusterSize + offsets[dir * 2] * (clusterSize - 1), + oy = cy * clusterSize + offsets[dir * 2 + 1] * (clusterSize - 1), + otherX = (moveDirs[dir * 2] * otherAverage + ox), + otherY = (moveDirs[dir * 2 + 1] * otherAverage + oy); - int - other = portals.items[j], - otherFrom = Point2.x(other), otherTo = Point2.y(other), - otherAverage = (otherFrom + otherTo) / 2, - ox = cx * clusterSize + offsets[dir * 2] * (clusterSize - 1), - oy = cy * clusterSize + offsets[dir * 2 + 1] * (clusterSize - 1), - otherX = (moveDirs[dir * 2] * otherAverage + ox), - otherY = (moveDirs[dir * 2 + 1] * otherAverage + oy); + float connectionCost = innerAstar( + team, cost, + minX, minY, maxX, maxY, + tileX + tileY * wwidth, + otherX + otherY * wwidth, + //TODO these are wrong and never actually trigger + (moveDirs[dir * 2] * otherFrom + ox), + (moveDirs[dir * 2 + 1] * otherFrom + oy), + (moveDirs[dir * 2] * otherTo + ox), + (moveDirs[dir * 2 + 1] * otherTo + oy) + ); - float connectionCost = innerAstar( - team, cost, - minX, minY, maxX, maxY, - tileX + tileY * wwidth, - otherX + otherY * wwidth, - //TODO these are wrong and never actually trigger - (moveDirs[dir * 2] * otherFrom + ox), - (moveDirs[dir * 2 + 1] * otherFrom + oy), - (moveDirs[dir * 2] * otherTo + ox), - (moveDirs[dir * 2 + 1] * otherTo + oy) - ); - - //better cost found, update and return - if(connectionCost != -1f && connectionCost < bestCost){ - bestPortalPair = Point2.pack(dir, j); - bestCost = connectionCost; - } + //better cost found, update and return + if(connectionCost != -1f && connectionCost < bestCost){ + bestPortalPair = Point2.pack(dir, j); + bestCost = connectionCost; } } - - if(bestPortalPair != Integer.MAX_VALUE){ - return makeNodeIndex(cx, cy, Point2.x(bestPortalPair), Point2.y(bestPortalPair)); - } } + if(bestPortalPair != Integer.MAX_VALUE){ + return makeNodeIndex(cx, cy, Point2.x(bestPortalPair), Point2.y(bestPortalPair)); + } + + return Integer.MAX_VALUE; } @@ -701,7 +719,6 @@ public class HierarchyPathFinder implements Runnable{ if(startNodeIndex == endNodeIndex){ result.clear(); result.add(startNodeIndex); - //TODO alloc return result; } @@ -766,15 +783,7 @@ public class HierarchyPathFinder implements Runnable{ return null; } - static void line(Vec2 a, Vec2 b){ - Fx.debugLine.at(a.x, a.y, 0f, Color.blue.cpy().a(0.1f), new Vec2[]{a.cpy(), b.cpy()}); - } - - static void line(Vec2 a, Vec2 b, Color color){ - Fx.debugLine.at(a.x, a.y, 0f, color, new Vec2[]{a.cpy(), b.cpy()}); - } - - void checkEdges(PathRequest request, int team, int pathCost, int current, int goal, int cx, int cy, LongSeq connections){ + private void checkEdges(PathRequest request, int team, int pathCost, int current, int goal, int cx, int cy, LongSeq connections){ for(int i = 0; i < connections.size; i++){ long con = connections.items[i]; float cost = IntraEdge.cost(con); @@ -788,9 +797,6 @@ public class HierarchyPathFinder implements Runnable{ request.frontier.add(next, newCost + clusterNodeHeuristic(team, pathCost, next, goal)); request.cameFrom.put(next, current); - - //TODO debug - line(nodeToVec(current, Tmp.v1), nodeToVec(next, Tmp.v2)); } } } @@ -806,7 +812,6 @@ public class HierarchyPathFinder implements Runnable{ int counter = 0; //actually do the flow field part - //TODO spread this out across many frames while(frontier.size > 0){ int tile = frontier.removeLast(); int baseX = tile % wwidth, baseY = tile / wwidth; @@ -858,9 +863,58 @@ public class HierarchyPathFinder implements Runnable{ } } - public void initializePathRequest(PathRequest request, int team, int unitX, int unitY, int goalX, int goalY){ - int costId = 0; - PathCost pcost = ControlPathfinder.costGround; + private void addFlowCluster(FieldCache cache, int cluster, boolean addingFrontier){ + addFlowCluster(cache, cluster % cwidth, cluster / cwidth, addingFrontier); + } + + private void addFlowCluster(FieldCache cache, int cx, int cy, boolean addingFrontier){ + //out of bounds + if(cx < 0 || cy < 0 || cx >= cwidth || cy >= cheight) return; + + var fields = cache.fields; + int key = cx + cy * cwidth; + + if(!fields.containsKey(key)){ + fields.put(key, new int[clusterSize * clusterSize]); + + //TODO: now, scan d4 for nearby clusters. + if(addingFrontier){ + + for(int dir = 0; dir < 4; dir++){ + int ox = cx + moveDirs[dir * 2], oy = cy + moveDirs[dir * 2 + 1]; + + if(ox < 0 || oy < 0 || ox >= cwidth || ox >= cheight) continue; + + var otherField = cache.fields.get(ox + oy * cwidth); + + if(otherField == null) continue; + + int + relOffset = (dir + 2) % 4, + movex = moveDirs[relOffset * 2], + movey = moveDirs[relOffset * 2 + 1], + otherx1 = offsets[relOffset * 2] * (clusterSize - 1), + othery1 = offsets[relOffset * 2 + 1] * (clusterSize - 1); + + //scan the edge of the cluster + for(int i = 0; i < clusterSize; i++){ + int x = otherx1 + movex * i, y = othery1 + movey * i; + + //check to make sure it's not 0 (uninitialized flowfield data) + if(otherField[x + y * clusterSize] != 0){ + int worldX = x + ox * clusterSize, worldY = y + oy * clusterSize; + + //add the world-relative position to the frontier, so it recalculates + cache.frontier.addFirst(worldX + worldY * wwidth); + } + } + } + } + } + } + + private void initializePathRequest(PathRequest request, int team, int costId, int unitX, int unitY, int goalX, int goalY){ + PathCost pcost = idToCost(costId); int goalPos = (goalX + goalY * wwidth); @@ -869,21 +923,22 @@ public class HierarchyPathFinder implements Runnable{ var nodePath = clusterAstar(request, costId, node, dest); - //TODO: how to reuse - FieldCache cache = this.fields.get(goalPos, () -> new FieldCache(pcost, team, goalPos)); + //TODO: how to reuse properly. what if the flowfields don't go through this position (the fields are finished?) how to incrementally extend the flowfield? + FieldCache cache = fields.get(goalPos); + //if true, extra values are added on the sides of existing field cells that face new cells. + boolean addingFrontier = true; - if(cache.frontier.isEmpty()){ + //create the cache if it doesn't exist, and initialize it + if(cache == null){ + fields.put(goalPos, cache = new FieldCache(pcost, team, goalPos)); cache.frontier.addFirst(goalPos); + addingFrontier = false; //when it's a new field, there is no need to add to the frontier to merge the flowfield } if(nodePath != null){ - - int fsize = clusterSize * clusterSize; int cx = unitX / clusterSize, cy = unitY / clusterSize; - var fields = cache.fields; - - fields.put(cx + cy * cwidth, new int[fsize]); + addFlowCluster(cache, cx, cy, addingFrontier); for(int i = -1; i < nodePath.size; i++){ int @@ -895,41 +950,35 @@ public class HierarchyPathFinder implements Runnable{ ox = cluster % cwidth + dx, oy = cluster / cwidth + dy; - //store current cluster in the path list - if(!fields.containsKey(cluster)){ - fields.put(cluster, new int[fsize]); - } + addFlowCluster(cache, cluster, addingFrontier); - //store directionals TODO out of bounds + //store directionals TODO can be out of bounds for(Point2 p : Geometry.d4){ - int other = cluster + p.x + p.y * cwidth; - if(!fields.containsKey(other)){ - fields.put(other, new int[fsize]); - } + addFlowCluster(cache, cluster + p.x + p.y * cwidth, addingFrontier); } //store directional/flipped version of cluster if(ox >= 0 && oy >= 0 && ox < cwidth && oy < cheight){ int other = ox + oy * cwidth; - if(!fields.containsKey(other)){ - fields.put(other, new int[fsize]); - } + + addFlowCluster(cache, other, addingFrontier); //store directionals again for(Point2 p : Geometry.d4){ - int other2 = other + p.x + p.y * cwidth; - if(!fields.containsKey(other2)){ - fields.put(other2, new int[fsize]); - } + addFlowCluster(cache, other + p.x + p.y * cwidth, addingFrontier); } } } } + } + private PathCost idToCost(int costId){ + return ControlPathfinder.costTypes.get(costId); } public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 out, boolean[] noResultFound){ int costId = 0; + PathCost cost = idToCost(costId); PathRequest request = unitRequests.get(unit); int @@ -939,7 +988,6 @@ public class HierarchyPathFinder implements Runnable{ //use existing request if it exists. if(request != null && request.destination == destPos){ - //TODO: collect old requests that have not been accessed in a while. not sure where. request.lastUpdateId = state.updateId; Tile tileOn = unit.tileOn(); @@ -962,9 +1010,9 @@ public class HierarchyPathFinder implements Runnable{ int packed = world.packArray(dx, dy); int otherCost = getCost(fieldCache.fields, dx, dy); - if(otherCost < value && (current == null || otherCost < tl) && passable(ControlPathfinder.costGround, unit.team.id, packed) && - !(point.x != 0 && point.y != 0 && (!passable(ControlPathfinder.costGround, unit.team.id, world.packArray(tileOn.x + point.x, tileOn.y)) || - (!passable(ControlPathfinder.costGround, unit.team.id, world.packArray(tileOn.x, tileOn.y + point.y)))))){ //diagonal corner trap + if(otherCost < value && (current == null || otherCost < tl) && passable(cost, unit.team.id, packed) && + !(point.x != 0 && point.y != 0 && (!passable(cost, unit.team.id, world.packArray(tileOn.x + point.x, tileOn.y)) || + (!passable(cost, unit.team.id, world.packArray(tileOn.x, tileOn.y + point.y)))))){ //diagonal corner trap current = other; tl = otherCost; @@ -986,7 +1034,7 @@ public class HierarchyPathFinder implements Runnable{ //on the pathfinding thread: initialize the request, meaning queue.post(() -> { - initializePathRequest(f, unit.team.id, unit.tileX(), unit.tileY(), destX, destY); + initializePathRequest(f, unit.team.id, costId, unit.tileX(), unit.tileY(), destX, destY); }); } @@ -1085,8 +1133,6 @@ public class HierarchyPathFinder implements Runnable{ clustersToInnerUpdate.clear(); clustersToUpdate.clear(); - //TODO: update everything else too - //each update time (not total!) no longer than maxUpdate for(FieldCache cache : fields.values()){ updateFields(cache, maxUpdate); From af7598dcc6c5800208bbe5e26cacf29b80c3939d Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 11 Nov 2023 14:20:48 -0500 Subject: [PATCH 017/348] Actually functional, but terrible --- .../src/mindustry/ai/HierarchyPathFinder.java | 287 ++++++++++++------ core/src/mindustry/ai/RtsAI.java | 2 + core/src/mindustry/ai/types/CommandAI.java | 5 +- 3 files changed, 206 insertions(+), 88 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 3732e4709b..67064abdb7 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -22,7 +22,7 @@ import static mindustry.ai.Pathfinder.*; //https://webdocs.cs.ualberta.ca/~mmueller/ps/hpastar.pdf //https://www.gameaipro.com/GameAIPro/GameAIPro_Chapter23_Crowd_Pathfinding_and_Steering_Using_Flow_Field_Tiles.pdf public class HierarchyPathFinder implements Runnable{ - private static final long maxUpdate = Time.millisToNanos(12); + private static final long maxUpdate = 100;//Time.millisToNanos(12); private static final int updateFPS = 30; private static final int updateInterval = 1000 / updateFPS; @@ -70,9 +70,10 @@ public class HierarchyPathFinder implements Runnable{ //TODO - it is written to on the pathfinding thread //maps position in world in (x + y * width format) to a cache of flow fields IntMap fields = new IntMap<>(); + //MAIN THREAD ONLY + Seq fieldList = new Seq<>(false); - - //these are for inner edge A* + //these are for inner edge A* (temporary!) IntFloatMap innerCosts = new IntFloatMap(); PathfindQueue innerFrontier = new PathfindQueue(); @@ -98,6 +99,10 @@ public class HierarchyPathFinder implements Runnable{ //main thread only! long lastUpdateId = state.updateId; + volatile boolean notFound = false; + + int lastTile; //TODO only re-raycast when unit moves a tile. + @Nullable Tile lastTargetTile; public PathRequest(Unit unit, int team, int destination){ this.unit = unit; @@ -135,10 +140,15 @@ public class HierarchyPathFinder implements Runnable{ stop(); //TODO: can the pathfinding thread even see these? + unitRequests = new ObjectMap<>(); + fields = new IntMap<>(); + fieldList = new Seq<>(false); + clusters = new Cluster[256][][]; cwidth = Mathf.ceil((float)world.width() / clusterSize); cheight = Mathf.ceil((float)world.height() / clusterSize); + start(); }); @@ -165,7 +175,7 @@ public class HierarchyPathFinder implements Runnable{ } }); - //TODO: recalculate affected flow fields? or just all of them? + //TODO: recalculate affected flow fields? or just all of them? how to reflow? }); //invalidate paths @@ -178,74 +188,74 @@ public class HierarchyPathFinder implements Runnable{ } } - for(var field : fields.values()){ + for(var field : fieldList){ //skipped N update -> drop it if(field.lastUpdateId <= state.updateId - 20){ //make sure it's only modified on the main thread...? but what about calling get() on this thread?? queue.post(() -> fields.remove(field.goalPos)); + Core.app.post(() -> fieldList.remove(field)); } } }); if(debug){ Events.run(Trigger.draw, () -> { - int team = Team.sharded.id; + int team = player.team().id; int cost = costGround; - if(clusters == null || clusters[cost] == null) return; - Draw.draw(Layer.overlayUI, () -> { Lines.stroke(1f); - for(int cx = 0; cx < cwidth; cx++){ - for(int cy = 0; cy < cheight; cy++){ - if(clusters[Team.sharded.id] == null || clusters[team][cost] == null) continue; - var cluster = clusters[team][cost][cy * cwidth + cx]; - if(cluster != null){ - Lines.stroke(0.5f); - Draw.color(Color.gray); - Lines.stroke(1f); + if(clusters[team] != null && clusters[team][cost] != null){ + for(int cx = 0; cx < cwidth; cx++){ + for(int cy = 0; cy < cheight; cy++){ - Lines.rect(cx * clusterSize * tilesize - tilesize/2f, cy * clusterSize * tilesize - tilesize/2f, clusterSize * tilesize, clusterSize * tilesize); + var cluster = clusters[team][cost][cy * cwidth + cx]; + if(cluster != null){ + Lines.stroke(0.5f); + Draw.color(Color.gray); + Lines.stroke(1f); + + Lines.rect(cx * clusterSize * tilesize - tilesize/2f, cy * clusterSize * tilesize - tilesize/2f, clusterSize * tilesize, clusterSize * tilesize); - for(int d = 0; d < 4; d++){ - IntSeq portals = cluster.portals[d]; - if(portals != null){ + for(int d = 0; d < 4; d++){ + IntSeq portals = cluster.portals[d]; + if(portals != null){ - for(int i = 0; i < portals.size; i++){ - int pos = portals.items[i]; - int from = Point2.x(pos), to = Point2.y(pos); - float width = tilesize * (Math.abs(from - to) + 1), height = tilesize; + for(int i = 0; i < portals.size; i++){ + int pos = portals.items[i]; + int from = Point2.x(pos), to = Point2.y(pos); + float width = tilesize * (Math.abs(from - to) + 1), height = tilesize; - portalToVec(cluster, cx, cy, d, i, Tmp.v1); + portalToVec(cluster, cx, cy, d, i, Tmp.v1); - Draw.color(Color.brown); - Lines.ellipse(30, Tmp.v1.x, Tmp.v1.y, width / 2f, height / 2f, d * 90f - 90f); + Draw.color(Color.brown); + Lines.ellipse(30, Tmp.v1.x, Tmp.v1.y, width / 2f, height / 2f, d * 90f - 90f); - LongSeq connections = cluster.portalConnections[d] == null ? null : cluster.portalConnections[d][i]; + LongSeq connections = cluster.portalConnections[d] == null ? null : cluster.portalConnections[d][i]; - if(connections != null){ - Draw.color(Color.forest); - for(int coni = 0; coni < connections.size; coni ++){ - long con = connections.items[coni]; + if(connections != null){ + Draw.color(Color.forest); + for(int coni = 0; coni < connections.size; coni ++){ + long con = connections.items[coni]; - portalToVec(cluster, cx, cy, IntraEdge.dir(con), IntraEdge.portal(con), Tmp.v2); + portalToVec(cluster, cx, cy, IntraEdge.dir(con), IntraEdge.portal(con), Tmp.v2); - float - x1 = Tmp.v1.x, y1 = Tmp.v1.y, - x2 = Tmp.v2.x, y2 = Tmp.v2.y, - mx = (cx * clusterSize + clusterSize / 2f) * tilesize, my = (cy * clusterSize + clusterSize / 2f) * tilesize; - //Lines.curve(x1, y1, mx, my, mx, my, x2, y2, 20); - Lines.line(x1, y1, x2, y2); + float + x1 = Tmp.v1.x, y1 = Tmp.v1.y, + x2 = Tmp.v2.x, y2 = Tmp.v2.y, + mx = (cx * clusterSize + clusterSize / 2f) * tilesize, my = (cy * clusterSize + clusterSize / 2f) * tilesize; + //Lines.curve(x1, y1, mx, my, mx, my, x2, y2, 20); + Lines.line(x1, y1, x2, y2); + } } } } } - } - //TODO draw connections. + //TODO draw connections. /* Draw.color(Color.magenta); @@ -257,28 +267,27 @@ public class HierarchyPathFinder implements Runnable{ //Lines.curve(x1, y1, mx, my, mx, my, x2, y2, 20); Lines.line(x1, y1, x2, y2); }*/ - } - } - } - - /* - if(fields != null){ - for(var entry : fields){ - int cx = entry.key % cwidth, cy = entry.key / cwidth; - for(int y = 0; y < clusterSize; y++){ - for(int x = 0; x < clusterSize; x++){ - int value = entry.value[x + y * clusterSize]; - Tmp.c1.a = 1f; - Lines.stroke(0.8f, Tmp.c1.fromHsv(value * 3f, 1f, 1f)); - Draw.alpha(0.5f); - Fill.square((x + cx * clusterSize) * tilesize, (y + cy * clusterSize) * tilesize, tilesize/2f); } } } } - */ - + for(var fields : fieldList){ + try{ + for(var entry : fields.fields){ + int cx = entry.key % cwidth, cy = entry.key / cwidth; + for(int y = 0; y < clusterSize; y++){ + for(int x = 0; x < clusterSize; x++){ + int value = entry.value[x + y * clusterSize]; + Tmp.c1.a = 1f; + Lines.stroke(0.8f, Tmp.c1.fromHsv(value * 3f, 1f, 1f)); + Draw.alpha(0.5f); + Fill.square((x + cx * clusterSize) * tilesize, (y + cy * clusterSize) * tilesize, tilesize / 2f); + } + } + } + }catch(Exception ignored){} //probably has some concurrency issues when iterating but I don't care, this is for debugging + } }); }); } @@ -816,7 +825,10 @@ public class HierarchyPathFinder implements Runnable{ int tile = frontier.removeLast(); int baseX = tile % wwidth, baseY = tile / wwidth; int curWeightIndex = (baseX / clusterSize) + (baseY / clusterSize) * cwidth; + + //TODO: how can this be null??? serious problem! int[] curWeights = fields.get(curWeightIndex); + if(curWeights == null) continue; int cost = curWeights[baseX % clusterSize + ((baseY % clusterSize) * clusterSize)]; @@ -921,6 +933,12 @@ public class HierarchyPathFinder implements Runnable{ int node = findClosestNode(team, costId, unitX, unitY); int dest = findClosestNode(team, costId, goalX, goalY); + if(dest == Integer.MAX_VALUE){ + request.notFound = true; + //no node found (TODO: invalid state??) + return; + } + var nodePath = clusterAstar(request, costId, node, dest); //TODO: how to reuse properly. what if the flowfields don't go through this position (the fields are finished?) how to incrementally extend the flowfield? @@ -931,6 +949,9 @@ public class HierarchyPathFinder implements Runnable{ //create the cache if it doesn't exist, and initialize it if(cache == null){ fields.put(goalPos, cache = new FieldCache(pcost, team, goalPos)); + FieldCache fcache = cache; + //register field in main thread for iteration + Core.app.post(() -> fieldList.add(fcache)); cache.frontier.addFirst(goalPos); addingFrontier = false; //when it's a new field, there is no need to add to the frontier to merge the flowfield } @@ -938,6 +959,7 @@ public class HierarchyPathFinder implements Runnable{ if(nodePath != null){ int cx = unitX / clusterSize, cy = unitY / clusterSize; + //TODO: instead of adding a bunch of clusters nobody cares about, dynamically add them later when needed addFlowCluster(cache, cx, cy, addingFrontier); for(int i = -1; i < nodePath.size; i++){ @@ -976,16 +998,35 @@ public class HierarchyPathFinder implements Runnable{ return ControlPathfinder.costTypes.get(costId); } - public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 out, boolean[] noResultFound){ + public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 mainDestination, Vec2 out, boolean[] noResultFound){ int costId = 0; PathCost cost = idToCost(costId); - PathRequest request = unitRequests.get(unit); int - destX = World.toTile(destination.x), - destY = World.toTile(destination.y) * wwidth, + team = unit.team.id, + tileX = unit.tileX(), + tileY = unit.tileY(), + destX = World.toTile(mainDestination.x), + destY = World.toTile(mainDestination.y), + actualDestX = World.toTile(destination.x), + actualDestY = World.toTile(destination.y), destPos = destX + destY * wwidth; + PathRequest request = unitRequests.get(unit); + + //if the destination can be trivially reached in a straight line, do that. + if(!raycast(team, cost, tileX, tileY, actualDestX, actualDestY)){ + out.set(destination); + return true; + } + + //TODO: the destination should not be the exact key. units have slightly different destinations based on offset from formation! + + //TODO raycast both diagonal edges to make sure it's reachable near corners + //var test = Geometry.raycastRect(unit.x, unit.y, current.worldx(), current.worldy(), Tmp.r1.setCentered(1f, 1f, tilesize).grow(7.8f)) != null; + + boolean any = false; + //use existing request if it exists. if(request != null && request.destination == destPos){ request.lastUpdateId = state.updateId; @@ -994,54 +1035,128 @@ public class HierarchyPathFinder implements Runnable{ //TODO: should fields be accessible from this thread? FieldCache fieldCache = fields.get(destPos); - if(tileOn != null && fieldCache != null){ - int value = getCost(fieldCache.fields, tileOn.x, tileOn.y); + if(fieldCache != null && tileOn != null){ + fieldCache.lastUpdateId = state.updateId; + int maxIterations = 30; //TODO higher/lower number? + int i = 0; - Tile current = null; - int tl = 0; - //TODO: use raycasting and iterate on this for N steps - for(Point2 point : Geometry.d8){ - int dx = tileOn.x + point.x, dy = tileOn.y + point.y; + if(tileOn.pos() != request.lastTile || request.lastTargetTile == null){ + //TODO tanks have weird behavior near edges of walls, as they try to avoid them - Tile other = world.tile(dx, dy); + while(i ++ < maxIterations && (!any || !raycast(team, cost, tileX, tileY, tileOn.x, tileOn.y))){ + //TODO: if there's no flowfield at this position, add it. + int value = getCost(fieldCache.fields, tileOn.x, tileOn.y); - if(other == null) continue; + Tile current = null; + int minCost = 0; + //TODO: use raycasting and iterate on this for N steps + for(Point2 point : Geometry.d8){ + int dx = tileOn.x + point.x, dy = tileOn.y + point.y; - int packed = world.packArray(dx, dy); - int otherCost = getCost(fieldCache.fields, dx, dy); + Tile other = world.tile(dx, dy); - if(otherCost < value && (current == null || otherCost < tl) && passable(cost, unit.team.id, packed) && - !(point.x != 0 && point.y != 0 && (!passable(cost, unit.team.id, world.packArray(tileOn.x + point.x, tileOn.y)) || - (!passable(cost, unit.team.id, world.packArray(tileOn.x, tileOn.y + point.y)))))){ //diagonal corner trap + if(other == null) continue; - current = other; - tl = otherCost; + int packed = world.packArray(dx, dy); + int otherCost = getCost(fieldCache.fields, dx, dy); + + //TODO: issue with hugging corners (you should not be able to move diagonally when there is a wall in the way) + + if(otherCost < value && (current == null || otherCost < minCost) && passable(cost, unit.team.id, packed) && + //diagonal corner trap + !( + (!passable(cost, team, world.packArray(tileOn.x + point.x, tileOn.y)) || + (!passable(cost, team, world.packArray(tileOn.x, tileOn.y + point.y)))) + ) + ){ + + current = other; + minCost = otherCost; + } + } + + if(!(current == null || minCost == impassable || (costId == costGround && current.dangerous() && !tileOn.dangerous()))){ + tileOn = current; + any = true; + }else{ + break; + } } + + request.lastTargetTile = any ? tileOn : null; } - if(!(current == null || tl == impassable || (costId == costGround && current.dangerous() && !tileOn.dangerous()))){ - out.set(current); + if(request.lastTargetTile != null){ + out.set(request.lastTargetTile); return true; } } - - }else{ + }else if(request == null){ //queue new request. - unitRequests.put(unit, request = new PathRequest(unit, unit.team.id, destPos)); + unitRequests.put(unit, request = new PathRequest(unit, team, destPos)); PathRequest f = request; - //on the pathfinding thread: initialize the request, meaning + //on the pathfinding thread: initialize the request queue.post(() -> { - initializePathRequest(f, unit.team.id, costId, unit.tileX(), unit.tileY(), destX, destY); + initializePathRequest(f, unit.team.id, costId, unit.tileX(), unit.tileY(), destX, destY); }); + + out.set(destination); + + return true; } - noResultFound[0] = true; + if(request != null){ + noResultFound[0] = request.notFound; + } return false; } + private static boolean raycast(int team, PathCost type, int x1, int y1, int x2, int y2){ + int ww = wwidth, wh = wheight; + int x = x1, dx = Math.abs(x2 - x), sx = x < x2 ? 1 : -1; + int y = y1, dy = Math.abs(y2 - y), sy = y < y2 ? 1 : -1; + int e2, err = dx - dy; + + while(x >= 0 && y >= 0 && x < ww && y < wh){ + if(avoid(team, type, x + y * wwidth)) return true; + if(x == x2 && y == y2) return false; + + //TODO no diagonals???? is this a good idea? + /* + //no diagonal ver + if(2 * err + dy > dx - 2 * err){ + err -= dy; + x += sx; + }else{ + err += dx; + y += sy; + }*/ + + //diagonal ver + e2 = 2 * err; + if(e2 > -dy){ + err -= dy; + x += sx; + } + + if(e2 < dx){ + err += dx; + y += sy; + } + + } + + return true; + } + + private static boolean avoid(int team, PathCost type, int tilePos){ + int cost = cost(team, type, tilePos); + return cost == impassable || cost >= 2; + } + private int getCost(IntMap fields, int x, int y){ int[] field = fields.get(x / clusterSize + (y / clusterSize) * cwidth); if(field == null){ diff --git a/core/src/mindustry/ai/RtsAI.java b/core/src/mindustry/ai/RtsAI.java index 6b37777c29..4d8698f6ba 100644 --- a/core/src/mindustry/ai/RtsAI.java +++ b/core/src/mindustry/ai/RtsAI.java @@ -77,6 +77,8 @@ public class RtsAI{ } public void update(){ + if(true) return; + if(timer.get(timeUpdate, 60f * 2f)){ assignSquads(); checkBuilding(); diff --git a/core/src/mindustry/ai/types/CommandAI.java b/core/src/mindustry/ai/types/CommandAI.java index 86a5a89447..de1f2f5cfc 100644 --- a/core/src/mindustry/ai/types/CommandAI.java +++ b/core/src/mindustry/ai/types/CommandAI.java @@ -219,7 +219,8 @@ public class CommandAI extends AIController{ } if(unit.isGrounded() && stance != UnitStance.ram){ - if(timer.get(timerTarget3, avoidInterval)){ + //TODO no blocking. + if(timer.get(timerTarget3, avoidInterval) && false){ Vec2 dstPos = Tmp.v1.trns(unit.rotation, unit.hitSize/2f); float max = unit.hitSize/2f; float radius = Math.max(7f, max); @@ -247,7 +248,7 @@ public class CommandAI extends AIController{ } //if you've spent 3 seconds stuck, something is wrong, move regardless - move = hpath.getPathPosition(unit, pathId, vecMovePos, vecOut, noFound) && (!blockingUnit || timeSpentBlocked > maxBlockTime); + move = hpath.getPathPosition(unit, pathId, vecMovePos, targetPos, vecOut, noFound) && (!blockingUnit || timeSpentBlocked > maxBlockTime); //we've reached the final point if the returned coordinate is equal to the supplied input isFinalPoint &= vecMovePos.epsilonEquals(vecOut, 4.1f); From 381fd12aad9ed3db919694662b46a6617d2e2de8 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 11 Nov 2023 19:59:14 -0500 Subject: [PATCH 018/348] Still terrible --- .../src/mindustry/ai/HierarchyPathFinder.java | 101 ++++++++++++------ core/src/mindustry/ai/RtsAI.java | 1 - .../src/mindustry/entities/comp/TankComp.java | 2 +- 3 files changed, 72 insertions(+), 32 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 67064abdb7..3d20e75dee 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -28,7 +28,7 @@ public class HierarchyPathFinder implements Runnable{ static final int clusterSize = 12; - static final boolean debug = true; + static final boolean debug = false; static final int[] offsets = { 1, 0, //right: bottom to top @@ -160,13 +160,11 @@ public class HierarchyPathFinder implements Runnable{ //is at the edge of a cluster; this means the portals may have changed. if(mx == 0 || my == 0 || mx == clusterSize - 1 || my == clusterSize - 1){ - if(mx == 0) queueClusterUpdate(cx - 1, cy); //left if(my == 0) queueClusterUpdate(cx, cy - 1); //bottom if(mx == clusterSize - 1) queueClusterUpdate(cx + 1, cy); //right if(my == clusterSize - 1) queueClusterUpdate(cx, cy + 1); //top - queueClusterUpdate(cx, cy); //TODO: recompute edge clusters too. }else{ @@ -190,7 +188,7 @@ public class HierarchyPathFinder implements Runnable{ for(var field : fieldList){ //skipped N update -> drop it - if(field.lastUpdateId <= state.updateId - 20){ + if(field.lastUpdateId <= state.updateId - 30){ //make sure it's only modified on the main thread...? but what about calling get() on this thread?? queue.post(() -> fields.remove(field.goalPos)); Core.app.post(() -> fieldList.remove(field)); @@ -893,7 +891,7 @@ public class HierarchyPathFinder implements Runnable{ if(addingFrontier){ for(int dir = 0; dir < 4; dir++){ - int ox = cx + moveDirs[dir * 2], oy = cy + moveDirs[dir * 2 + 1]; + int ox = cx + nextOffsets[dir * 2], oy = cy + nextOffsets[dir * 2 + 1]; if(ox < 0 || oy < 0 || ox >= cwidth || ox >= cheight) continue; @@ -913,11 +911,15 @@ public class HierarchyPathFinder implements Runnable{ int x = otherx1 + movex * i, y = othery1 + movey * i; //check to make sure it's not 0 (uninitialized flowfield data) - if(otherField[x + y * clusterSize] != 0){ + if(otherField[x + y * clusterSize] > 0){ int worldX = x + ox * clusterSize, worldY = y + oy * clusterSize; //add the world-relative position to the frontier, so it recalculates cache.frontier.addFirst(worldX + worldY * wwidth); + + if(debug){ + Core.app.post(() -> Fx.placeBlock.at(worldX *tilesize, worldY * tilesize, 1f)); + } } } } @@ -976,7 +978,7 @@ public class HierarchyPathFinder implements Runnable{ //store directionals TODO can be out of bounds for(Point2 p : Geometry.d4){ - addFlowCluster(cache, cluster + p.x + p.y * cwidth, addingFrontier); + //addFlowCluster(cache, cluster + p.x + p.y * cwidth, addingFrontier); } //store directional/flipped version of cluster @@ -987,7 +989,7 @@ public class HierarchyPathFinder implements Runnable{ //store directionals again for(Point2 p : Geometry.d4){ - addFlowCluster(cache, other + p.x + p.y * cwidth, addingFrontier); + //addFlowCluster(cache, other + p.x + p.y * cwidth, addingFrontier); } } } @@ -1023,7 +1025,7 @@ public class HierarchyPathFinder implements Runnable{ //TODO: the destination should not be the exact key. units have slightly different destinations based on offset from formation! //TODO raycast both diagonal edges to make sure it's reachable near corners - //var test = Geometry.raycastRect(unit.x, unit.y, current.worldx(), current.worldy(), Tmp.r1.setCentered(1f, 1f, tilesize).grow(7.8f)) != null; + // boolean any = false; @@ -1039,18 +1041,20 @@ public class HierarchyPathFinder implements Runnable{ fieldCache.lastUpdateId = state.updateId; int maxIterations = 30; //TODO higher/lower number? int i = 0; + //TODO: tanks do not reach max speed when near a tile they are flowing to. if(tileOn.pos() != request.lastTile || request.lastTargetTile == null){ //TODO tanks have weird behavior near edges of walls, as they try to avoid them + boolean anyNearSolid = false; - while(i ++ < maxIterations && (!any || !raycast(team, cost, tileX, tileY, tileOn.x, tileOn.y))){ - //TODO: if there's no flowfield at this position, add it. - int value = getCost(fieldCache.fields, tileOn.x, tileOn.y); + //find the next tile until one near a solid block is discovered + while(i ++ < maxIterations && !anyNearSolid){ + int value = getCost(fieldCache, tileOn.x, tileOn.y); Tile current = null; int minCost = 0; - //TODO: use raycasting and iterate on this for N steps - for(Point2 point : Geometry.d8){ + for(int dir = 0; dir < 8; dir ++){ + Point2 point = Geometry.d8[dir]; int dx = tileOn.x + point.x, dy = tileOn.y + point.y; Tile other = world.tile(dx, dy); @@ -1058,32 +1062,51 @@ public class HierarchyPathFinder implements Runnable{ if(other == null) continue; int packed = world.packArray(dx, dy); - int otherCost = getCost(fieldCache.fields, dx, dy); + int otherCost = getCost(fieldCache, dx, dy), relCost = otherCost - value; - //TODO: issue with hugging corners (you should not be able to move diagonally when there is a wall in the way) + if(relCost > 2 || otherCost <= 0){ + anyNearSolid = true; + } - if(otherCost < value && (current == null || otherCost < minCost) && passable(cost, unit.team.id, packed) && + if(relCost == 7 || relCost == 8) otherCost = value + 1; + + //check for corner preventing movement + if((checkCorner(unit, tileOn, other, dir - 1) || checkCorner(unit, tileOn, other, dir + 1)) && + (checkSolid(unit, tileOn, dir - 2) || checkSolid(unit, tileOn, dir + 2))){ //there must be a tile to the left or right to keep the unit from going back and forth forever + + //keep moving even if it's blocked + any = true; + continue; + } + + if(otherCost < value && otherCost != impassable && (otherCost != 0 || packed == destPos) && (current == null || otherCost < minCost) && passable(cost, unit.team.id, packed) && //diagonal corner trap !( (!passable(cost, team, world.packArray(tileOn.x + point.x, tileOn.y)) || (!passable(cost, team, world.packArray(tileOn.x, tileOn.y + point.y)))) ) ){ - current = other; minCost = otherCost; } } - if(!(current == null || minCost == impassable || (costId == costGround && current.dangerous() && !tileOn.dangerous()))){ + if(!(current == null || (costId == costGround && current.dangerous() && !tileOn.dangerous()))){ tileOn = current; any = true; + + if(current.array() == destPos){ + break; + } }else{ break; } } request.lastTargetTile = any ? tileOn : null; + if(debug && tileOn != null){ + Fx.placeBlock.at(tileOn.worldx(), tileOn.worldy(), 1); + } } if(request.lastTargetTile != null){ @@ -1108,12 +1131,38 @@ public class HierarchyPathFinder implements Runnable{ return true; } - if(request != null){ - noResultFound[0] = request.notFound; - } + noResultFound[0] = request.notFound; return false; } + private boolean checkSolid(Unit unit, Tile tile, int dir){ + var p = Geometry.d8[Mathf.mod(dir, 8)]; + return !unit.canPass(tile.x + p.x, tile.y + p.y); + } + + private boolean checkCorner(Unit unit, Tile tile, Tile next, int dir){ + Tile other = tile.nearby(Geometry.d8[Mathf.mod(dir, 8)]); + if(other == null){ + return true; + } + + if(!unit.canPass(other.x, other.y)){ + return Geometry.raycastRect(unit.x, unit.y, next.worldx(), next.worldy(), Tmp.r1.setCentered(other.worldx(), other.worldy(), tilesize).grow(Math.min(unit.hitSize * 0.66f, 7.6f))) != null; + } + + return false; + } + + private int getCost(FieldCache cache, int x, int y){ + int[] field = cache.fields.get(x / clusterSize + (y / clusterSize) * cwidth); + if(field == null){ + //request a new flow cluster if one wasn't found; this may be a spammed a bit, but the function will return early once it's created the first time + queue.post(() -> addFlowCluster(cache, x / clusterSize, y / clusterSize, true)); + return -1; + } + return field[(x % clusterSize) + (y % clusterSize) * clusterSize]; + } + private static boolean raycast(int team, PathCost type, int x1, int y1, int x2, int y2){ int ww = wwidth, wh = wheight; int x = x1, dx = Math.abs(x2 - x), sx = x < x2 ? 1 : -1; @@ -1157,14 +1206,6 @@ public class HierarchyPathFinder implements Runnable{ return cost == impassable || cost >= 2; } - private int getCost(IntMap fields, int x, int y){ - int[] field = fields.get(x / clusterSize + (y / clusterSize) * cwidth); - if(field == null){ - return -1; - } - return field[(x % clusterSize) + (y % clusterSize) * clusterSize]; - } - private static boolean passable(PathCost cost, int team, int pos){ int amount = cost.getCost(team, pathfinder.tiles[pos]); //edge case: naval reports costs of 6000+ for non-liquids, even though they are not technically passable diff --git a/core/src/mindustry/ai/RtsAI.java b/core/src/mindustry/ai/RtsAI.java index 4d8698f6ba..92025da099 100644 --- a/core/src/mindustry/ai/RtsAI.java +++ b/core/src/mindustry/ai/RtsAI.java @@ -77,7 +77,6 @@ public class RtsAI{ } public void update(){ - if(true) return; if(timer.get(timeUpdate, 60f * 2f)){ assignSquads(); diff --git a/core/src/mindustry/entities/comp/TankComp.java b/core/src/mindustry/entities/comp/TankComp.java index 38946709ac..986a17cb56 100644 --- a/core/src/mindustry/entities/comp/TankComp.java +++ b/core/src/mindustry/entities/comp/TankComp.java @@ -51,7 +51,7 @@ abstract class TankComp implements Posc, Flyingc, Hitboxc, Unitc, ElevationMovec } //calculate overlapping tiles so it slows down when going "over" walls - int r = Math.max(Math.round(hitSize * 0.6f / tilesize), 1); + int r = Math.max((int)(hitSize * 0.6f / tilesize), 0); int solids = 0, total = (r*2+1)*(r*2+1); for(int dx = -r; dx <= r; dx++){ From 997702b9de207bd3e33269135e42c9e768baa283 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 12 Nov 2023 12:34:03 -0500 Subject: [PATCH 019/348] progress --- .../src/mindustry/ai/HierarchyPathFinder.java | 44 +++++++++---------- core/src/mindustry/ai/types/LogicAI.java | 3 +- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 3d20e75dee..741285a5f1 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -28,7 +28,7 @@ public class HierarchyPathFinder implements Runnable{ static final int clusterSize = 12; - static final boolean debug = false; + static final boolean debug = true; static final int[] offsets = { 1, 0, //right: bottom to top @@ -104,7 +104,7 @@ public class HierarchyPathFinder implements Runnable{ int lastTile; //TODO only re-raycast when unit moves a tile. @Nullable Tile lastTargetTile; - public PathRequest(Unit unit, int team, int destination){ + PathRequest(Unit unit, int team, int destination){ this.unit = unit; this.team = team; this.destination = destination; @@ -125,7 +125,7 @@ public class HierarchyPathFinder implements Runnable{ //TODO: how are the nodes merged? CAN they be merged? - public FieldCache(PathCost cost, int team, int goalPos){ + FieldCache(PathCost cost, int team, int goalPos){ this.cost = cost; this.team = team; this.goalPos = goalPos; @@ -851,7 +851,10 @@ public class HierarchyPathFinder implements Runnable{ //can't move back to the goal if(newPos == goalPos) continue; + if(dx - clx * clusterSize < 0 || dy - cly * clusterSize < 0) continue; + int newPosArray = (dx - clx * clusterSize) + (dy - cly * clusterSize) * clusterSize; + int otherCost = pcost.getCost(team, pathfinder.tiles[newPos]); int oldCost = weights[newPosArray]; @@ -961,7 +964,6 @@ public class HierarchyPathFinder implements Runnable{ if(nodePath != null){ int cx = unitX / clusterSize, cy = unitY / clusterSize; - //TODO: instead of adding a bunch of clusters nobody cares about, dynamically add them later when needed addFlowCluster(cache, cx, cy, addingFrontier); for(int i = -1; i < nodePath.size; i++){ @@ -976,21 +978,11 @@ public class HierarchyPathFinder implements Runnable{ addFlowCluster(cache, cluster, addingFrontier); - //store directionals TODO can be out of bounds - for(Point2 p : Geometry.d4){ - //addFlowCluster(cache, cluster + p.x + p.y * cwidth, addingFrontier); - } - //store directional/flipped version of cluster if(ox >= 0 && oy >= 0 && ox < cwidth && oy < cheight){ int other = ox + oy * cwidth; addFlowCluster(cache, other, addingFrontier); - - //store directionals again - for(Point2 p : Geometry.d4){ - //addFlowCluster(cache, other + p.x + p.y * cwidth, addingFrontier); - } } } } @@ -1000,7 +992,7 @@ public class HierarchyPathFinder implements Runnable{ return ControlPathfinder.costTypes.get(costId); } - public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 mainDestination, Vec2 out, boolean[] noResultFound){ + public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 mainDestination, Vec2 out, @Nullable boolean[] noResultFound){ int costId = 0; PathCost cost = idToCost(costId); @@ -1022,11 +1014,6 @@ public class HierarchyPathFinder implements Runnable{ return true; } - //TODO: the destination should not be the exact key. units have slightly different destinations based on offset from formation! - - //TODO raycast both diagonal edges to make sure it's reachable near corners - // - boolean any = false; //use existing request if it exists. @@ -1103,8 +1090,10 @@ public class HierarchyPathFinder implements Runnable{ } } + //TODO: there are some serious issues with tileOn and the raycast position... + //TODO intense vibration request.lastTargetTile = any ? tileOn : null; - if(debug && tileOn != null){ + if(debug && tileOn != null && false){ Fx.placeBlock.at(tileOn.worldx(), tileOn.worldy(), 1); } } @@ -1131,7 +1120,9 @@ public class HierarchyPathFinder implements Runnable{ return true; } - noResultFound[0] = request.notFound; + if(noResultFound != null){ + noResultFound[0] = request.notFound; + } return false; } @@ -1231,6 +1222,13 @@ public class HierarchyPathFinder implements Runnable{ return cost.getCost(team, pathfinder.tiles[tilePos]); } + private void clusterChanged(int team, int pathCost, int cx, int cy){ + //TODO very important: invalidate paths! + //reset all flowfields that contain this cluster + //remove all paths that contain this cluster + //VERY important: don't replace all the data. + } + private void updateClustersComplete(int clusterIndex){ for(int team = 0; team < clusters.length; team++){ var dim1 = clusters[team]; @@ -1241,6 +1239,7 @@ public class HierarchyPathFinder implements Runnable{ var cluster = dim2[clusterIndex]; if(cluster != null){ updateCluster(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth); + clusterChanged(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth); } } } @@ -1258,6 +1257,7 @@ public class HierarchyPathFinder implements Runnable{ var cluster = dim2[clusterIndex]; if(cluster != null){ updateInnerEdges(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth, cluster); + clusterChanged(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth); } } } diff --git a/core/src/mindustry/ai/types/LogicAI.java b/core/src/mindustry/ai/types/LogicAI.java index d334f9dec1..cb7250cd7b 100644 --- a/core/src/mindustry/ai/types/LogicAI.java +++ b/core/src/mindustry/ai/types/LogicAI.java @@ -3,7 +3,6 @@ package mindustry.ai.types; import arc.math.*; import arc.struct.*; import arc.util.*; -import mindustry.*; import mindustry.ai.*; import mindustry.entities.units.*; import mindustry.gen.*; @@ -86,7 +85,7 @@ public class LogicAI extends AIController{ if(unit.isFlying()){ moveTo(Tmp.v1.set(moveX, moveY), 1f, 30f); }else{ - if(Vars.controlPath.getPathPosition(unit, lastPathId, Tmp.v2.set(moveX, moveY), Tmp.v1, null)){ + if(hpath.getPathPosition(unit, lastPathId, Tmp.v2.set(moveX, moveY), Tmp.v2, Tmp.v1, null)){ moveTo(Tmp.v1, 1f, Tmp.v2.epsilonEquals(Tmp.v1, 4.1f) ? 30f : 0f); } } From 587ff8bb467b2bd0f9f870669f1582f8d3833dab Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 15 Nov 2023 20:03:55 -0500 Subject: [PATCH 020/348] Proper path cost support --- core/src/mindustry/ai/HierarchyPathFinder.java | 5 +++-- core/src/mindustry/ai/types/CommandAI.java | 2 +- core/src/mindustry/ai/types/LogicAI.java | 2 +- core/src/mindustry/entities/comp/UnitComp.java | 2 +- core/src/mindustry/type/UnitType.java | 5 +++++ gradle.properties | 2 +- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 741285a5f1..31b8f1cdba 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -992,8 +992,8 @@ public class HierarchyPathFinder implements Runnable{ return ControlPathfinder.costTypes.get(costId); } - public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 mainDestination, Vec2 out, @Nullable boolean[] noResultFound){ - int costId = 0; + public boolean getPathPosition(Unit unit, Vec2 destination, Vec2 mainDestination, Vec2 out, @Nullable boolean[] noResultFound){ + int costId = unit.type.pathCostId; PathCost cost = idToCost(costId); int @@ -1006,6 +1006,7 @@ public class HierarchyPathFinder implements Runnable{ actualDestY = World.toTile(destination.y), destPos = destX + destY * wwidth; + //TODO: what if the destination is different...? PathRequest request = unitRequests.get(unit); //if the destination can be trivially reached in a straight line, do that. diff --git a/core/src/mindustry/ai/types/CommandAI.java b/core/src/mindustry/ai/types/CommandAI.java index de1f2f5cfc..eb05008d27 100644 --- a/core/src/mindustry/ai/types/CommandAI.java +++ b/core/src/mindustry/ai/types/CommandAI.java @@ -248,7 +248,7 @@ public class CommandAI extends AIController{ } //if you've spent 3 seconds stuck, something is wrong, move regardless - move = hpath.getPathPosition(unit, pathId, vecMovePos, targetPos, vecOut, noFound) && (!blockingUnit || timeSpentBlocked > maxBlockTime); + move = hpath.getPathPosition(unit, vecMovePos, targetPos, vecOut, noFound) && (!blockingUnit || timeSpentBlocked > maxBlockTime); //we've reached the final point if the returned coordinate is equal to the supplied input isFinalPoint &= vecMovePos.epsilonEquals(vecOut, 4.1f); diff --git a/core/src/mindustry/ai/types/LogicAI.java b/core/src/mindustry/ai/types/LogicAI.java index cb7250cd7b..2ac8840d3f 100644 --- a/core/src/mindustry/ai/types/LogicAI.java +++ b/core/src/mindustry/ai/types/LogicAI.java @@ -85,7 +85,7 @@ public class LogicAI extends AIController{ if(unit.isFlying()){ moveTo(Tmp.v1.set(moveX, moveY), 1f, 30f); }else{ - if(hpath.getPathPosition(unit, lastPathId, Tmp.v2.set(moveX, moveY), Tmp.v2, Tmp.v1, null)){ + if(hpath.getPathPosition(unit, Tmp.v2.set(moveX, moveY), Tmp.v2, Tmp.v1, null)){ moveTo(Tmp.v1, 1f, Tmp.v2.epsilonEquals(Tmp.v1, 4.1f) ? 30f : 0f); } } diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index 4ff6b3168f..d6483f8f25 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -403,7 +403,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I return type.allowLegStep && type.legPhysicsLayer ? PhysicsProcess.layerLegs : isGrounded() ? PhysicsProcess.layerGround : PhysicsProcess.layerFlying; } - /** @return pathfinder path type for calculating costs */ + /** @return pathfinder path type for calculating costs. This is used for wave AI only. (TODO: remove) */ public int pathType(){ return Pathfinder.costGround; } diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index a8623a6d65..3a2cf891b8 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -286,6 +286,8 @@ public class UnitType extends UnlockableContent implements Senseable{ /** Function used for calculating cost of moving with ControlPathfinder. Does not affect "normal" flow field pathfinding. */ public @Nullable PathCost pathCost; + /** ID for path cost, to be used in the control path finder. This is the value that actually matters; do not assign manually. Set in init(). */ + public int pathCostId; /** A sample of the unit that this type creates. Do not modify! */ public @Nullable Unit sample; @@ -689,6 +691,9 @@ public class UnitType extends UnlockableContent implements Senseable{ ControlPathfinder.costGround; } + pathCostId = ControlPathfinder.costTypes.indexOf(pathCost); + if(pathCostId == -1) pathCostId = 0; + if(flying){ envEnabled |= Env.space; } diff --git a/gradle.properties b/gradle.properties index 090150dba9..da998b39a0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=e2fdbab477 +archash=96dd703d5d From c8209d568ffcda72898b57d8dbe8a824cc3c14d5 Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 15 Nov 2023 20:50:24 -0500 Subject: [PATCH 021/348] progress --- .../src/mindustry/ai/HierarchyPathFinder.java | 56 ++++++++++++++----- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 31b8f1cdba..4c7b4c738f 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -81,6 +81,12 @@ public class HierarchyPathFinder implements Runnable{ IntSet clustersToUpdate = new IntSet(); IntSet clustersToInnerUpdate = new IntSet(); + //invalid request implies invalid field as well. + //there should be a list of temporary evicted fields... + //TODO path requests should not be actually invalidated until the paths they refer to have completed processing. + // - also, only do this every couple of seconds at least. + ObjectSet invalidRequests = new ObjectSet<>(); + /** Current pathfinding thread */ @Nullable Thread thread; @@ -90,18 +96,22 @@ public class HierarchyPathFinder implements Runnable{ final int destination, team; //resulting path of nodes final IntSeq resultPath = new IntSeq(); + //node index -> total cost - final IntFloatMap costs = new IntFloatMap(); - //node index (NodeIndex struct) -> node it came from TODO merge them - final IntIntMap cameFrom = new IntIntMap(); + IntFloatMap costs = new IntFloatMap(); + //node index (NodeIndex struct) -> node it came from TODO merge them, make properties of FieldCache? + IntIntMap cameFrom = new IntIntMap(); //frontier for A* - final PathfindQueue frontier = new PathfindQueue(); + PathfindQueue frontier = new PathfindQueue(); //main thread only! long lastUpdateId = state.updateId; - volatile boolean notFound = false; - int lastTile; //TODO only re-raycast when unit moves a tile. + //both threads + volatile boolean notFound = false; + volatile boolean invalidated = false; + + int lastTile; @Nullable Tile lastTargetTile; PathRequest(Unit unit, int team, int destination){ @@ -181,6 +191,7 @@ public class HierarchyPathFinder implements Runnable{ for(var req : unitRequests.values()){ //skipped N update -> drop it if(req.lastUpdateId <= state.updateId - 10){ + req.invalidated = true; //concurrent modification! Core.app.post(() -> unitRequests.remove(req.unit)); } @@ -729,14 +740,17 @@ public class HierarchyPathFinder implements Runnable{ return result; } + var team = request.team; + + if(request.costs == null) request.costs = new IntFloatMap(); + if(request.cameFrom == null) request.cameFrom = new IntIntMap(); + if(request.frontier == null) request.frontier = new PathfindQueue(); + + //note: these are NOT cleared, it is assumed that this function cleans up after itself at the end + //is this a good idea? don't know, might hammer the GC with unnecessary objects too var costs = request.costs; var cameFrom = request.cameFrom; var frontier = request.frontier; - var team = request.team; - - frontier.clear(); - costs.clear(); - cameFrom.clear(); cameFrom.put(startNodeIndex, startNodeIndex); costs.put(startNodeIndex, 0); @@ -774,6 +788,12 @@ public class HierarchyPathFinder implements Runnable{ } } + //null them out, so they get GC'ed later + //there's no reason to keep them around and waste memory, since this path may never be recalculated + request.costs = null; + request.cameFrom = null; + request.frontier = null; + if(foundEnd){ result.clear(); @@ -1006,7 +1026,6 @@ public class HierarchyPathFinder implements Runnable{ actualDestY = World.toTile(destination.y), destPos = destX + destY * wwidth; - //TODO: what if the destination is different...? PathRequest request = unitRequests.get(unit); //if the destination can be trivially reached in a straight line, do that. @@ -1029,7 +1048,6 @@ public class HierarchyPathFinder implements Runnable{ fieldCache.lastUpdateId = state.updateId; int maxIterations = 30; //TODO higher/lower number? int i = 0; - //TODO: tanks do not reach max speed when near a tile they are flowing to. if(tileOn.pos() != request.lastTile || request.lastTargetTile == null){ //TODO tanks have weird behavior near edges of walls, as they try to avoid them @@ -1091,8 +1109,7 @@ public class HierarchyPathFinder implements Runnable{ } } - //TODO: there are some serious issues with tileOn and the raycast position... - //TODO intense vibration + //TODO: there are some serious issues with tileOn and the raycast position, intense vibration request.lastTargetTile = any ? tileOn : null; if(debug && tileOn != null && false){ Fx.placeBlock.at(tileOn.worldx(), tileOn.worldy(), 1); @@ -1228,6 +1245,15 @@ public class HierarchyPathFinder implements Runnable{ //reset all flowfields that contain this cluster //remove all paths that contain this cluster //VERY important: don't replace all the data. + + int index = cx + cy * cwidth; + + //TODO go through each path request: + // - if it contains this cluster in its field: + // - mark for it to be recomputed next frame in a Set (so it doesn't happen twice!) + // - recomputing should invalidate the flowfield + // - invalidations should be batched every few seconds (let's say, 2) + } private void updateClustersComplete(int clusterIndex){ From 565d3313a4e0df5d06e4f74f4a6b21bf046d494c Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 16 Nov 2023 09:56:58 -0500 Subject: [PATCH 022/348] Invalidate path requests --- .../src/mindustry/ai/HierarchyPathFinder.java | 117 +++++++++++++++--- 1 file changed, 97 insertions(+), 20 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 4c7b4c738f..c68f031086 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -23,8 +23,9 @@ import static mindustry.ai.Pathfinder.*; //https://www.gameaipro.com/GameAIPro/GameAIPro_Chapter23_Crowd_Pathfinding_and_Steering_Using_Flow_Field_Tiles.pdf public class HierarchyPathFinder implements Runnable{ private static final long maxUpdate = 100;//Time.millisToNanos(12); + private static final int updateStepInterval = 20;//200; private static final int updateFPS = 30; - private static final int updateInterval = 1000 / updateFPS; + private static final int updateInterval = 1000 / updateFPS, invalidateCheckInterval = 1000; static final int clusterSize = 12; @@ -65,6 +66,8 @@ public class HierarchyPathFinder implements Runnable{ //individual requests based on unit - MAIN THREAD ONLY ObjectMap unitRequests = new ObjectMap<>(); + Seq threadPathRequests = new Seq<>(false); + //TODO: very dangerous usage; //TODO - it is accessed from the main thread //TODO - it is written to on the pathfinding thread @@ -81,10 +84,7 @@ public class HierarchyPathFinder implements Runnable{ IntSet clustersToUpdate = new IntSet(); IntSet clustersToInnerUpdate = new IntSet(); - //invalid request implies invalid field as well. - //there should be a list of temporary evicted fields... - //TODO path requests should not be actually invalidated until the paths they refer to have completed processing. - // - also, only do this every couple of seconds at least. + //PATHFINDING THREAD - requests that should be recomputed ObjectSet invalidRequests = new ObjectSet<>(); /** Current pathfinding thread */ @@ -93,16 +93,16 @@ public class HierarchyPathFinder implements Runnable{ //path requests are per-unit static class PathRequest{ final Unit unit; - final int destination, team; + final int destination, team, costId; //resulting path of nodes final IntSeq resultPath = new IntSeq(); //node index -> total cost - IntFloatMap costs = new IntFloatMap(); + @Nullable IntFloatMap costs = new IntFloatMap(); //node index (NodeIndex struct) -> node it came from TODO merge them, make properties of FieldCache? - IntIntMap cameFrom = new IntIntMap(); + @Nullable IntIntMap cameFrom = new IntIntMap(); //frontier for A* - PathfindQueue frontier = new PathfindQueue(); + @Nullable PathfindQueue frontier = new PathfindQueue(); //main thread only! long lastUpdateId = state.updateId; @@ -110,12 +110,15 @@ public class HierarchyPathFinder implements Runnable{ //both threads volatile boolean notFound = false; volatile boolean invalidated = false; + //old field assigned before everything was recomputed + @Nullable volatile FieldCache oldCache; int lastTile; @Nullable Tile lastTargetTile; - PathRequest(Unit unit, int team, int destination){ + PathRequest(Unit unit, int team, int costId, int destination){ this.unit = unit; + this.costId = costId; this.team = team; this.destination = destination; } @@ -193,6 +196,7 @@ public class HierarchyPathFinder implements Runnable{ if(req.lastUpdateId <= state.updateId - 10){ req.invalidated = true; //concurrent modification! + queue.post(() -> threadPathRequests.remove(req)); Core.app.post(() -> unitRequests.remove(req.unit)); } } @@ -887,7 +891,7 @@ public class HierarchyPathFinder implements Runnable{ } //every N iterations, check the time spent - this prevents extra calls to nano time, which itself is slow - if(nsToRun >= 0 && (counter++) >= 200){ + if(nsToRun >= 0 && (counter++) >= updateStepInterval){ counter = 0; if(Time.timeSinceNanos(start) >= nsToRun){ return; @@ -1045,6 +1049,12 @@ public class HierarchyPathFinder implements Runnable{ FieldCache fieldCache = fields.get(destPos); if(fieldCache != null && tileOn != null){ + FieldCache old = request.oldCache; + //nullify the old field to be GCed, as it cannot be relevant anymore (this path is complete) + if(fieldCache.frontier.isEmpty() && old != null){ + request.oldCache = null; + } + fieldCache.lastUpdateId = state.updateId; int maxIterations = 30; //TODO higher/lower number? int i = 0; @@ -1055,7 +1065,7 @@ public class HierarchyPathFinder implements Runnable{ //find the next tile until one near a solid block is discovered while(i ++ < maxIterations && !anyNearSolid){ - int value = getCost(fieldCache, tileOn.x, tileOn.y); + int value = getCost(fieldCache, old, tileOn.x, tileOn.y); Tile current = null; int minCost = 0; @@ -1068,7 +1078,7 @@ public class HierarchyPathFinder implements Runnable{ if(other == null) continue; int packed = world.packArray(dx, dy); - int otherCost = getCost(fieldCache, dx, dy), relCost = otherCost - value; + int otherCost = getCost(fieldCache, old, dx, dy), relCost = otherCost - value; if(relCost > 2 || otherCost <= 0){ anyNearSolid = true; @@ -1124,13 +1134,14 @@ public class HierarchyPathFinder implements Runnable{ }else if(request == null){ //queue new request. - unitRequests.put(unit, request = new PathRequest(unit, team, destPos)); + unitRequests.put(unit, request = new PathRequest(unit, team, costId, destPos)); PathRequest f = request; //on the pathfinding thread: initialize the request queue.post(() -> { - initializePathRequest(f, unit.team.id, costId, unit.tileX(), unit.tileY(), destX, destY); + threadPathRequests.add(f); + recalculatePath(f); }); out.set(destination); @@ -1144,6 +1155,10 @@ public class HierarchyPathFinder implements Runnable{ return false; } + private void recalculatePath(PathRequest request){ + initializePathRequest(request, request.team, request.costId, request.unit.tileX(), request.unit.tileY(), request.destination % wwidth, request.destination / wwidth); + } + private boolean checkSolid(Unit unit, Tile tile, int dir){ var p = Geometry.d8[Mathf.mod(dir, 8)]; return !unit.canPass(tile.x + p.x, tile.y + p.y); @@ -1162,12 +1177,21 @@ public class HierarchyPathFinder implements Runnable{ return false; } - private int getCost(FieldCache cache, int x, int y){ + private int getCost(FieldCache cache, FieldCache old, int x, int y){ + //fall back to the old flowfield when possible - it's best not to use partial results from the base cache + if(old != null){ + return getCost(old, x, y, false); + } + return getCost(cache, x, y, true); + } + + private int getCost(FieldCache cache, int x, int y, boolean requeue){ int[] field = cache.fields.get(x / clusterSize + (y / clusterSize) * cwidth); if(field == null){ + if(!requeue) return 0; //request a new flow cluster if one wasn't found; this may be a spammed a bit, but the function will return early once it's created the first time queue.post(() -> addFlowCluster(cache, x / clusterSize, y / clusterSize, true)); - return -1; + return 0; } return field[(x % clusterSize) + (y % clusterSize) * clusterSize]; } @@ -1250,9 +1274,16 @@ public class HierarchyPathFinder implements Runnable{ //TODO go through each path request: // - if it contains this cluster in its field: - // - mark for it to be recomputed next frame in a Set (so it doesn't happen twice!) - // - recomputing should invalidate the flowfield - // - invalidations should be batched every few seconds (let's say, 2) + // - DONE mark for it to be recomputed next frame in a Set (so it doesn't happen twice!) + // - DONE recomputing should invalidate the flowfield + // - recomputing should save the old flowfield Somewhere + // - DONE invalidations should be batched every few seconds (let's say, 2) + for(var req : threadPathRequests){ + var field = fields.get(req.destination); + if(field != null && field.fields.containsKey(index)){ + invalidRequests.add(req); + } + } } @@ -1294,10 +1325,13 @@ public class HierarchyPathFinder implements Runnable{ @Override public void run(){ + long lastInvalidCheck = Time.millis() + invalidateCheckInterval; + while(true){ if(net.client()) return; try{ + if(state.isPlaying()){ queue.run(); @@ -1316,6 +1350,49 @@ public class HierarchyPathFinder implements Runnable{ clustersToInnerUpdate.clear(); clustersToUpdate.clear(); + //periodically check for invalidated paths + if(Time.timeSinceMillis(lastInvalidCheck) > invalidateCheckInterval){ + lastInvalidCheck = Time.millis(); + + var it = invalidRequests.iterator(); + while(it.hasNext()){ + var request = it.next(); + + //invalid request, ignore it + if(request.invalidated){ + it.remove(); + continue; + } + + var field = fields.get(request.destination); + + if(field != null){ + //it's only worth recalculating a path when the current frontier has finished; otherwise the unit will be following something incomplete. + if(field.frontier.isEmpty()){ + + //remove the field, to be recalculated next update one recalculatePath is processed + fields.remove(field.goalPos); + Core.app.post(() -> fieldList.remove(field)); + + //once the field is invalidated, make sure that all the requests that have it stored in their 'old' field, so units don't stutter during recalculations + for(var otherRequest : threadPathRequests){ + if(otherRequest.destination == request.destination){ + otherRequest.oldCache = field; + } + } + + //the recalculation is done next update, so multiple path requests in the same batch don't end up removing and recalculating the field multiple times. + queue.post(() -> recalculatePath(request)); + //it has been processed. + it.remove(); + } + }else{ //there's no field, presumably because a previous request already invalidated it. + queue.post(() -> recalculatePath(request)); + it.remove(); + } + } + } + //each update time (not total!) no longer than maxUpdate for(FieldCache cache : fields.values()){ updateFields(cache, maxUpdate); From 7a3476b9f7ed456aca9416c7a261688cbafda437 Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 16 Nov 2023 09:58:01 -0500 Subject: [PATCH 023/348] Debug property --- core/src/mindustry/ai/HierarchyPathFinder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index c68f031086..b77badf5e0 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -29,7 +29,7 @@ public class HierarchyPathFinder implements Runnable{ static final int clusterSize = 12; - static final boolean debug = true; + static final boolean debug = OS.hasProp("mindustry.debug"); static final int[] offsets = { 1, 0, //right: bottom to top From af71006f3f2b5654ccb01dddedff56205316b389 Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 16 Nov 2023 09:58:25 -0500 Subject: [PATCH 024/348] Removed flowfield time restrictions --- core/src/mindustry/ai/HierarchyPathFinder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index b77badf5e0..0c4ec1f695 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -22,8 +22,8 @@ import static mindustry.ai.Pathfinder.*; //https://webdocs.cs.ualberta.ca/~mmueller/ps/hpastar.pdf //https://www.gameaipro.com/GameAIPro/GameAIPro_Chapter23_Crowd_Pathfinding_and_Steering_Using_Flow_Field_Tiles.pdf public class HierarchyPathFinder implements Runnable{ - private static final long maxUpdate = 100;//Time.millisToNanos(12); - private static final int updateStepInterval = 20;//200; + private static final long maxUpdate = Time.millisToNanos(12); + private static final int updateStepInterval = 200; private static final int updateFPS = 30; private static final int updateInterval = 1000 / updateFPS, invalidateCheckInterval = 1000; From c72eb5bd9c4f27496ae5bcb5085ae9ee353f0fee Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 16 Nov 2023 11:00:31 -0500 Subject: [PATCH 025/348] Cleanup --- .../src/mindustry/ai/HierarchyPathFinder.java | 127 ++++++++---------- core/src/mindustry/ai/types/CommandAI.java | 2 +- 2 files changed, 58 insertions(+), 71 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 0c4ec1f695..16904bdfd9 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -29,7 +29,7 @@ public class HierarchyPathFinder implements Runnable{ static final int clusterSize = 12; - static final boolean debug = OS.hasProp("mindustry.debug"); + static final boolean debug = false;//OS.hasProp("mindustry.debug"); static final int[] offsets = { 1, 0, //right: bottom to top @@ -145,6 +145,12 @@ public class HierarchyPathFinder implements Runnable{ } } + static class Cluster{ + IntSeq[] portals = new IntSeq[4]; + //maps rotation + index of portal to list of IntraEdge objects + LongSeq[][] portalConnections = new LongSeq[4][]; + } + public HierarchyPathFinder(){ Events.on(ResetEvent.class, event -> stop()); @@ -257,9 +263,7 @@ public class HierarchyPathFinder implements Runnable{ float x1 = Tmp.v1.x, y1 = Tmp.v1.y, - x2 = Tmp.v2.x, y2 = Tmp.v2.y, - mx = (cx * clusterSize + clusterSize / 2f) * tilesize, my = (cy * clusterSize + clusterSize / 2f) * tilesize; - //Lines.curve(x1, y1, mx, my, mx, my, x2, y2, 20); + x2 = Tmp.v2.x, y2 = Tmp.v2.y; Lines.line(x1, y1, x2, y2); } @@ -267,19 +271,6 @@ public class HierarchyPathFinder implements Runnable{ } } } - - //TODO draw connections. - - /* - Draw.color(Color.magenta); - for(var con : cluster.cons){ - float - x1 = Point2.x(con.posFrom) * tilesize, y1 = Point2.y(con.posFrom) * tilesize, - x2 = Point2.x(con.posTo) * tilesize, y2 = Point2.y(con.posTo) * tilesize, - mx = (cx * clusterSize + clusterSize/2f) * tilesize, my = (cy * clusterSize + clusterSize/2f) * tilesize; - //Lines.curve(x1, y1, mx, my, mx, my, x2, y2, 20); - Lines.line(x1, y1, x2, y2); - }*/ } } } @@ -312,24 +303,7 @@ public class HierarchyPathFinder implements Runnable{ } } - static void line(Vec2 a, Vec2 b){ - Fx.debugLine.at(a.x, a.y, 0f, Color.blue.cpy().a(0.1f), new Vec2[]{a.cpy(), b.cpy()}); - } - - static void line(Vec2 a, Vec2 b, Color color){ - Fx.debugLine.at(a.x, a.y, 0f, color, new Vec2[]{a.cpy(), b.cpy()}); - } - - //DEBUGGING ONLY - Vec2 nodeToVec(int current, Vec2 out){ - portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), out); - return out; - } - - void portalToVec(int pathCost, int cluster, int direction, int portalIndex, Vec2 out){ - portalToVec(clusters[Team.sharded.id][pathCost][cluster], cluster % cwidth, cluster / cwidth, direction, portalIndex, out); - } - + //debugging only! void portalToVec(Cluster cluster, int cx, int cy, int direction, int portalIndex, Vec2 out){ int pos = cluster.portals[direction].items[portalIndex]; int from = Point2.x(pos), to = Point2.y(pos); @@ -595,13 +569,24 @@ public class HierarchyPathFinder implements Runnable{ costs.put(startPos, 0); frontier.add(startPos, 0); + if(goalX2 < goalX1){ + int tmp = goalX1; + goalX1 = goalX2; + goalX2 = tmp; + } + + if(goalY2 < goalY1){ + int tmp = goalY1; + goalY1 = goalY2; + goalY2 = tmp; + } + while(frontier.size > 0){ int current = frontier.poll(); int cx = current % wwidth, cy = current / wwidth; //found the goal (it's in the portal rectangle) - //TODO portal rectangle approach does not work, making this slower than it should be if((cx >= goalX1 && cy >= goalY1 && cx <= goalX2 && cy <= goalY2) || current == goalPos){ return costs.get(current); } @@ -610,10 +595,7 @@ public class HierarchyPathFinder implements Runnable{ int newx = cx + point.x, newy = cy + point.y; int next = newx + wwidth * newy; - if(newx > maxX || newy > maxY || newx < minX || newy < minY) continue; - - //TODO fallback mode for enemy walls or whatever - if(tcost(team, cost, next) == impassable) continue; + if(newx > maxX || newy > maxY || newx < minX || newy < minY || tcost(team, cost, next) == impassable) continue; float add = tileCost(team, cost, current, next); @@ -688,7 +670,6 @@ public class HierarchyPathFinder implements Runnable{ minX, minY, maxX, maxY, tileX + tileY * wwidth, otherX + otherY * wwidth, - //TODO these are wrong and never actually trigger (moveDirs[dir * 2] * otherFrom + ox), (moveDirs[dir * 2 + 1] * otherFrom + oy), (moveDirs[dir * 2] * otherTo + ox), @@ -914,9 +895,7 @@ public class HierarchyPathFinder implements Runnable{ if(!fields.containsKey(key)){ fields.put(key, new int[clusterSize * clusterSize]); - //TODO: now, scan d4 for nearby clusters. if(addingFrontier){ - for(int dir = 0; dir < 4; dir++){ int ox = cx + nextOffsets[dir * 2], oy = cy + nextOffsets[dir * 2 + 1]; @@ -970,7 +949,6 @@ public class HierarchyPathFinder implements Runnable{ var nodePath = clusterAstar(request, costId, node, dest); - //TODO: how to reuse properly. what if the flowfields don't go through this position (the fields are finished?) how to incrementally extend the flowfield? FieldCache cache = fields.get(goalPos); //if true, extra values are added on the sides of existing field cells that face new cells. boolean addingFrontier = true; @@ -1033,7 +1011,8 @@ public class HierarchyPathFinder implements Runnable{ PathRequest request = unitRequests.get(unit); //if the destination can be trivially reached in a straight line, do that. - if(!raycast(team, cost, tileX, tileY, actualDestX, actualDestY)){ + //near the destination, standard raycasting tends to break down, so use the more permissive 'near' variant that doesn't take into account edges of walls + if(unit.within(destination, tilesize * 2.5f) ? !raycastNear(team, cost, tileX, tileY, actualDestX, actualDestY) : !raycast(team, cost, tileX, tileY, actualDestX, actualDestY)){ out.set(destination); return true; } @@ -1056,6 +1035,7 @@ public class HierarchyPathFinder implements Runnable{ } fieldCache.lastUpdateId = state.updateId; + //TODO: 30 iterations every frame is incredibly slow and terrible and drops the FPS on mobile devices significantly. int maxIterations = 30; //TODO higher/lower number? int i = 0; @@ -1084,9 +1064,9 @@ public class HierarchyPathFinder implements Runnable{ anyNearSolid = true; } - if(relCost == 7 || relCost == 8) otherCost = value + 1; + if(relCost == 7) otherCost = value; - //check for corner preventing movement + //check for corner preventing movement TODO will break with the new last tile pos stuff if((checkCorner(unit, tileOn, other, dir - 1) || checkCorner(unit, tileOn, other, dir + 1)) && (checkSolid(unit, tileOn, dir - 2) || checkSolid(unit, tileOn, dir + 2))){ //there must be a tile to the left or right to keep the unit from going back and forth forever @@ -1095,11 +1075,11 @@ public class HierarchyPathFinder implements Runnable{ continue; } - if(otherCost < value && otherCost != impassable && (otherCost != 0 || packed == destPos) && (current == null || otherCost < minCost) && passable(cost, unit.team.id, packed) && + if(otherCost < value && otherCost != impassable && (otherCost != 0 || packed == destPos) && (current == null || otherCost < minCost) && passable(unit.team.id, cost, packed) && //diagonal corner trap !( - (!passable(cost, team, world.packArray(tileOn.x + point.x, tileOn.y)) || - (!passable(cost, team, world.packArray(tileOn.x, tileOn.y + point.y)))) + (!passable(team, cost, world.packArray(tileOn.x + point.x, tileOn.y)) || + (!passable(team, cost, world.packArray(tileOn.x, tileOn.y + point.y)))) ) ){ current = other; @@ -1119,7 +1099,6 @@ public class HierarchyPathFinder implements Runnable{ } } - //TODO: there are some serious issues with tileOn and the raycast position, intense vibration request.lastTargetTile = any ? tileOn : null; if(debug && tileOn != null && false){ Fx.placeBlock.at(tileOn.worldx(), tileOn.worldy(), 1); @@ -1128,6 +1107,8 @@ public class HierarchyPathFinder implements Runnable{ if(request.lastTargetTile != null){ out.set(request.lastTargetTile); + //TODO: broken + //request.lastTile = tileOn.pos(); return true; } } @@ -1206,17 +1187,6 @@ public class HierarchyPathFinder implements Runnable{ if(avoid(team, type, x + y * wwidth)) return true; if(x == x2 && y == y2) return false; - //TODO no diagonals???? is this a good idea? - /* - //no diagonal ver - if(2 * err + dy > dx - 2 * err){ - err -= dy; - x += sx; - }else{ - err += dx; - y += sy; - }*/ - //diagonal ver e2 = 2 * err; if(e2 > -dy){ @@ -1228,6 +1198,29 @@ public class HierarchyPathFinder implements Runnable{ err += dx; y += sy; } + } + + return true; + } + + private static boolean raycastNear(int team, PathCost type, int x1, int y1, int x2, int y2){ + int ww = wwidth, wh = wheight; + int x = x1, dx = Math.abs(x2 - x), sx = x < x2 ? 1 : -1; + int y = y1, dy = Math.abs(y2 - y), sy = y < y2 ? 1 : -1; + int e2, err = dx - dy; + + while(x >= 0 && y >= 0 && x < ww && y < wh){ + if(!passable(team, type, x + y * wwidth)) return true; + if(x == x2 && y == y2) return false; + + //no diagonal ver + if(2 * err + dy > dx - 2 * err){ + err -= dy; + x += sx; + }else{ + err += dx; + y += sy; + } } @@ -1239,7 +1232,7 @@ public class HierarchyPathFinder implements Runnable{ return cost == impassable || cost >= 2; } - private static boolean passable(PathCost cost, int team, int pos){ + private static boolean passable(int team, PathCost cost, int pos){ int amount = cost.getCost(team, pathfinder.tiles[pos]); //edge case: naval reports costs of 6000+ for non-liquids, even though they are not technically passable return amount != impassable && !(cost == costTypes.get(costNaval) && amount >= 6000); @@ -1280,7 +1273,7 @@ public class HierarchyPathFinder implements Runnable{ // - DONE invalidations should be batched every few seconds (let's say, 2) for(var req : threadPathRequests){ var field = fields.get(req.destination); - if(field != null && field.fields.containsKey(index)){ + if((field != null && field.fields.containsKey(index)) || req.notFound){ invalidRequests.add(req); } } @@ -1411,12 +1404,6 @@ public class HierarchyPathFinder implements Runnable{ } } - static class Cluster{ - IntSeq[] portals = new IntSeq[4]; - //maps rotation + index of portal to list of IntraEdge objects - LongSeq[][] portalConnections = new LongSeq[4][]; - } - @Struct static class IntraEdgeStruct{ @StructField(8) diff --git a/core/src/mindustry/ai/types/CommandAI.java b/core/src/mindustry/ai/types/CommandAI.java index eb05008d27..2e2a24851d 100644 --- a/core/src/mindustry/ai/types/CommandAI.java +++ b/core/src/mindustry/ai/types/CommandAI.java @@ -219,7 +219,7 @@ public class CommandAI extends AIController{ } if(unit.isGrounded() && stance != UnitStance.ram){ - //TODO no blocking. + //TODO: blocking is disabled, doesn't work well if(timer.get(timerTarget3, avoidInterval) && false){ Vec2 dstPos = Tmp.v1.trns(unit.rotation, unit.hitSize/2f); float max = unit.hitSize/2f; From 171da8cf9bbbd130c317cbb6dd615d90a7b2e21f Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 17 Nov 2023 16:48:03 -0500 Subject: [PATCH 026/348] Minor optimizations --- .../src/mindustry/ai/HierarchyPathFinder.java | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 16904bdfd9..8fb7dd5334 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -113,6 +113,8 @@ public class HierarchyPathFinder implements Runnable{ //old field assigned before everything was recomputed @Nullable volatile FieldCache oldCache; + boolean lastRaycastResult = false; + int lastRaycastTile, lastWorldUpdate; int lastTile; @Nullable Tile lastTargetTile; @@ -1002,6 +1004,7 @@ public class HierarchyPathFinder implements Runnable{ team = unit.team.id, tileX = unit.tileX(), tileY = unit.tileY(), + packedPos = world.packArray(tileX, tileY), destX = World.toTile(mainDestination.x), destY = World.toTile(mainDestination.y), actualDestX = World.toTile(destination.x), @@ -1010,9 +1013,23 @@ public class HierarchyPathFinder implements Runnable{ PathRequest request = unitRequests.get(unit); + int lastRaycastTile = request == null || world.tileChanges != request.lastWorldUpdate ? -1 : request.lastRaycastTile; + boolean raycastResult = request != null && request.lastRaycastResult; + + //cache raycast results to run every time the world updates, and every tile the unit crosses + if(lastRaycastTile != packedPos){ + //near the destination, standard raycasting tends to break down, so use the more permissive 'near' variant that doesn't take into account edges of walls + raycastResult = unit.within(destination, tilesize * 2.5f) ? !raycastNear(team, cost, tileX, tileY, actualDestX, actualDestY) : !raycast(team, cost, tileX, tileY, actualDestX, actualDestY); + + if(request != null){ + request.lastRaycastTile = packedPos; + request.lastRaycastResult = raycastResult; + request.lastWorldUpdate = world.tileChanges; + } + } + //if the destination can be trivially reached in a straight line, do that. - //near the destination, standard raycasting tends to break down, so use the more permissive 'near' variant that doesn't take into account edges of walls - if(unit.within(destination, tilesize * 2.5f) ? !raycastNear(team, cost, tileX, tileY, actualDestX, actualDestY) : !raycast(team, cost, tileX, tileY, actualDestX, actualDestY)){ + if(raycastResult){ out.set(destination); return true; } @@ -1023,7 +1040,7 @@ public class HierarchyPathFinder implements Runnable{ if(request != null && request.destination == destPos){ request.lastUpdateId = state.updateId; - Tile tileOn = unit.tileOn(); + Tile tileOn = unit.tileOn(), initialTileOn = tileOn; //TODO: should fields be accessible from this thread? FieldCache fieldCache = fields.get(destPos); @@ -1038,8 +1055,10 @@ public class HierarchyPathFinder implements Runnable{ //TODO: 30 iterations every frame is incredibly slow and terrible and drops the FPS on mobile devices significantly. int maxIterations = 30; //TODO higher/lower number? int i = 0; + boolean recalc = false; - if(tileOn.pos() != request.lastTile || request.lastTargetTile == null){ + //TODO last pos can change if the flowfield changes. + if(initialTileOn.pos() != request.lastTile || request.lastTargetTile == null){ //TODO tanks have weird behavior near edges of walls, as they try to avoid them boolean anyNearSolid = false; @@ -1064,12 +1083,11 @@ public class HierarchyPathFinder implements Runnable{ anyNearSolid = true; } - if(relCost == 7) otherCost = value; - - //check for corner preventing movement TODO will break with the new last tile pos stuff + //check for corner preventing movement if((checkCorner(unit, tileOn, other, dir - 1) || checkCorner(unit, tileOn, other, dir + 1)) && (checkSolid(unit, tileOn, dir - 2) || checkSolid(unit, tileOn, dir + 2))){ //there must be a tile to the left or right to keep the unit from going back and forth forever + recalc = true; //keep moving even if it's blocked any = true; continue; @@ -1100,15 +1118,14 @@ public class HierarchyPathFinder implements Runnable{ } request.lastTargetTile = any ? tileOn : null; - if(debug && tileOn != null && false){ + if(true && tileOn != null){ Fx.placeBlock.at(tileOn.worldx(), tileOn.worldy(), 1); } } if(request.lastTargetTile != null){ out.set(request.lastTargetTile); - //TODO: broken - //request.lastTile = tileOn.pos(); + request.lastTile = recalc ? -1 : initialTileOn.pos(); return true; } } @@ -1207,7 +1224,8 @@ public class HierarchyPathFinder implements Runnable{ int ww = wwidth, wh = wheight; int x = x1, dx = Math.abs(x2 - x), sx = x < x2 ? 1 : -1; int y = y1, dy = Math.abs(y2 - y), sy = y < y2 ? 1 : -1; - int e2, err = dx - dy; + int err = dx - dy; + while(x >= 0 && y >= 0 && x < ww && y < wh){ if(!passable(team, type, x + y * wwidth)) return true; From 9fab556e81e2aa3354601b9aeec073b8550e0af0 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 17 Nov 2023 17:04:47 -0500 Subject: [PATCH 027/348] Debugging disabled --- core/src/mindustry/ai/HierarchyPathFinder.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 8fb7dd5334..40526d0cad 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -1052,8 +1052,7 @@ public class HierarchyPathFinder implements Runnable{ } fieldCache.lastUpdateId = state.updateId; - //TODO: 30 iterations every frame is incredibly slow and terrible and drops the FPS on mobile devices significantly. - int maxIterations = 30; //TODO higher/lower number? + int maxIterations = 30; //TODO higher/lower number? is this still too slow? int i = 0; boolean recalc = false; @@ -1118,7 +1117,7 @@ public class HierarchyPathFinder implements Runnable{ } request.lastTargetTile = any ? tileOn : null; - if(true && tileOn != null){ + if(debug && tileOn != null){ Fx.placeBlock.at(tileOn.worldx(), tileOn.worldy(), 1); } } From 320b2ae54d76a1fdfe027ab0c4533f444790c7e5 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 27 Nov 2023 06:53:43 -0500 Subject: [PATCH 028/348] Cleanup --- core/src/mindustry/ai/HierarchyPathFinder.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 40526d0cad..972261fef3 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -1275,19 +1275,8 @@ public class HierarchyPathFinder implements Runnable{ } private void clusterChanged(int team, int pathCost, int cx, int cy){ - //TODO very important: invalidate paths! - //reset all flowfields that contain this cluster - //remove all paths that contain this cluster - //VERY important: don't replace all the data. - int index = cx + cy * cwidth; - //TODO go through each path request: - // - if it contains this cluster in its field: - // - DONE mark for it to be recomputed next frame in a Set (so it doesn't happen twice!) - // - DONE recomputing should invalidate the flowfield - // - recomputing should save the old flowfield Somewhere - // - DONE invalidations should be batched every few seconds (let's say, 2) for(var req : threadPathRequests){ var field = fields.get(req.destination); if((field != null && field.fields.containsKey(index)) || req.notFound){ From 2539bb01955ccc3b7c77e245f2cb8a64ec722a5f Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 21 Mar 2024 10:32:43 -0400 Subject: [PATCH 029/348] Fixed #9663 --- core/src/mindustry/input/DesktopInput.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/input/DesktopInput.java b/core/src/mindustry/input/DesktopInput.java index 2c196660d4..031f5af2bf 100644 --- a/core/src/mindustry/input/DesktopInput.java +++ b/core/src/mindustry/input/DesktopInput.java @@ -568,7 +568,7 @@ public class DesktopInput extends InputHandler{ schematicY += shiftY; } - if(Core.input.keyTap(Binding.deselect) && !isPlacing() && player.unit().plans.isEmpty() && !commandMode){ + if(Core.input.keyTap(Binding.deselect) && !ui.minimapfrag.shown() && !isPlacing() && player.unit().plans.isEmpty() && !commandMode){ player.unit().mineTile = null; } From 32ae555ad6b2ee5579642f460d6de1ec4af7e3db Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 22 Mar 2024 15:02:59 -0400 Subject: [PATCH 030/348] Fixed power links in editor sector generate --- core/src/mindustry/editor/MapEditor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/editor/MapEditor.java b/core/src/mindustry/editor/MapEditor.java index 37a10cca89..38839e6108 100644 --- a/core/src/mindustry/editor/MapEditor.java +++ b/core/src/mindustry/editor/MapEditor.java @@ -74,7 +74,7 @@ public class MapEditor{ for(int i = 0; i < tiles.width * tiles.height; i++){ Tile tile = tiles.geti(i); var build = tile.build; - if(build != null){ + if(build != null && tile.isCenter()){ builds.add(build); } tiles.seti(i, new EditorTile(tile.x, tile.y, tile.floorID(), tile.overlayID(), build == null ? tile.blockID() : 0)); From 8fe08268924be759343c0855b11c785372f58fb1 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 22 Mar 2024 15:14:52 -0400 Subject: [PATCH 031/348] Better base core randomization --- core/src/mindustry/maps/generators/BaseGenerator.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/mindustry/maps/generators/BaseGenerator.java b/core/src/mindustry/maps/generators/BaseGenerator.java index e38c5206fe..9671a83a42 100644 --- a/core/src/mindustry/maps/generators/BaseGenerator.java +++ b/core/src/mindustry/maps/generators/BaseGenerator.java @@ -47,20 +47,20 @@ public class BaseGenerator{ if(bases.cores.isEmpty()) return; Mathf.rand.setSeed(sector.id); + Mathf.rand.nextDouble(); float bracketRange = 0.17f; float baseChance = Mathf.lerp(0.7f, 2.1f, difficulty); int wallAngle = 70; //180 for full coverage double resourceChance = 0.5 * baseChance; double nonResourceChance = 0.002 * baseChance; - BasePart coreschem = bases.cores.getFrac(difficulty); int passes = difficulty < 0.4 ? 1 : difficulty < 0.8 ? 3 : 5; Block wall = getDifficultyWall(1, difficulty), wallLarge = getDifficultyWall(2, difficulty); for(Tile tile : cores){ tile.clearOverlay(); - Schematics.placeLoadout(coreschem.schematic, tile.x, tile.y, team, false); + Schematics.placeLoadout(bases.cores.getFrac((difficulty + Mathf.rand.range(0.4f)) / 1.4f).schematic, tile.x, tile.y, team, false); //fill core with every type of item (even non-material) Building entity = tile.build; @@ -81,7 +81,7 @@ public class BaseGenerator{ tryPlace(parts.getFrac(difficulty + Mathf.range(bracketRange)), tile.x, tile.y, team); } }else if(Mathf.chance(nonResourceChance)){ - tryPlace(bases.parts.getFrac(Mathf.random(1f)), tile.x, tile.y, team); + tryPlace(bases.parts.getFrac(Mathf.rand.random(1f)), tile.x, tile.y, team); } }); } @@ -235,7 +235,7 @@ public class BaseGenerator{ set(placed, item); } - Tile rand = world.tiles.getc(ex + Mathf.range(1), ey + Mathf.range(1)); + Tile rand = world.tiles.getc(ex + Mathf.rand.range(1), ey + Mathf.rand.range(1)); if(rand.floor().hasSurface()){ //random ores nearby to make it look more natural set(rand, item); From e97dc7a9603bf62caa7ba50fec137c4145a2e1f5 Mon Sep 17 00:00:00 2001 From: PolarStar <107398572+PoIarStar@users.noreply.github.com> Date: Sun, 24 Mar 2024 22:03:08 +0300 Subject: [PATCH 032/348] Update servers_v7.json (#9671) Merging servers --- servers_v7.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/servers_v7.json b/servers_v7.json index d95da17b9b..ad5b28bdfb 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -28,13 +28,9 @@ "name": "Crux's Revelations", "address": ["de-free-01.hosts.optikservers.com:31528","de-prem-01.hosts.optikservers.com:35922"] }, - { - "name": "Chilldustry", - "address": ["trelesco.xyz"] - }, { "name": "CMS Empire", - "address": ["94.250.250.252","95.84.198.97"] + "address": ["trelesco.xyz","94.250.250.252","95.84.198.97"] }, { "name": "ShardDustry Eventos", From 4cacb79ccd2e6aaee7fc726c54b5af68327ea3e1 Mon Sep 17 00:00:00 2001 From: MEEPofFaith <54301439+MEEPofFaith@users.noreply.github.com> Date: Mon, 25 Mar 2024 10:31:03 -0700 Subject: [PATCH 033/348] Auto-add produce to liquids (#9666) --- core/src/mindustry/mod/ContentParser.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/mod/ContentParser.java b/core/src/mindustry/mod/ContentParser.java index 093f9fd98f..40da31c7d1 100644 --- a/core/src/mindustry/mod/ContentParser.java +++ b/core/src/mindustry/mod/ContentParser.java @@ -1102,8 +1102,8 @@ public class ContentParser{ } //all items have a produce requirement unless already specified - if(object instanceof Item i && !node.objectives.contains(o -> o instanceof Produce p && p.content == i)){ - node.objectives.add(new Produce(i)); + if((unlock instanceof Item || unlock instanceof Liquid) && !node.objectives.contains(o -> o instanceof Produce p && p.content == unlock)){ + node.objectives.add(new Produce(unlock)); } //remove old node from parent From 88d37763daa3fbc37258dd93dd764f77133f1566 Mon Sep 17 00:00:00 2001 From: PolarStar <107398572+PoIarStar@users.noreply.github.com> Date: Mon, 25 Mar 2024 21:11:30 +0300 Subject: [PATCH 034/348] Update servers_v7.json (#9674) deleted outdated addresses --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index ad5b28bdfb..f003996b48 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -30,7 +30,7 @@ }, { "name": "CMS Empire", - "address": ["trelesco.xyz","94.250.250.252","95.84.198.97"] + "address": ["trelesco.xyz"] }, { "name": "ShardDustry Eventos", From ce22552bca1b3db64c1ce0fbca17b640cc66fe96 Mon Sep 17 00:00:00 2001 From: GaviTSRA <61122293+GaviTSRA@users.noreply.github.com> Date: Mon, 25 Mar 2024 21:41:56 +0100 Subject: [PATCH 035/348] Add TSR Sandbox to v7 serverlist (#9672) --- servers_v7.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/servers_v7.json b/servers_v7.json index f003996b48..3fdb7f9aa5 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -1,4 +1,8 @@ [ + { + "name": "TSR Network", + "address": ["de-prem-01.hosts.optikservers.com:35526"] + }, { "name": "Toast Mindustry", "address": ["192.3.139.5:6567", "192.3.139.5:6568", "192.3.139.5:6569", "104.168.64.154:6567", "104.168.64.154:6568", "104.168.64.154:6569"] From 9c3ff565082ba28be54b03b6986dc4a78e6dceff Mon Sep 17 00:00:00 2001 From: Hunggaming128 <160811008+Hunggaming128@users.noreply.github.com> Date: Tue, 26 Mar 2024 03:42:05 +0700 Subject: [PATCH 036/348] Update servers_v7.json (#9669) --- servers_v7.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/servers_v7.json b/servers_v7.json index 3fdb7f9aa5..7ef103cac1 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -293,12 +293,12 @@ "name": "MineCore", "address": ["194.247.42.11:27792", "194.247.42.11:27977", "194.247.42.61:27989", "194.247.42.181:28514"] }, - { - "name": "Sever.VN", - "address": ["192.168.1.2:6567"] - }, { "name": "NPTS", "address": ["81.94.159.242"] + }, + { + "name": "Skywar.VN", + "address": ["nur-de-01.blued.host:25736"] } ] From 631e8a4fa47c5a98f8bc470a38f7b00fddbefcdd Mon Sep 17 00:00:00 2001 From: Jason <131086642+JasonP01@users.noreply.github.com> Date: Mon, 25 Mar 2024 22:42:14 +0200 Subject: [PATCH 037/348] fix poly (#9662) Signed-off-by: Jason <131086642+JasonP01@users.noreply.github.com> --- core/src/mindustry/content/UnitTypes.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index 089f5d4e39..c9200be5e1 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -1318,6 +1318,7 @@ public class UnitTypes{ healPercent = 5.5f; collidesTeam = true; + reflectable = false; backColor = Pal.heal; trailColor = Pal.heal; }}; From e595627895e2b332d44aeae78ae9ac493cc49021 Mon Sep 17 00:00:00 2001 From: Elixias <61173114+LixieWulf@users.noreply.github.com> Date: Mon, 25 Mar 2024 17:20:26 -0600 Subject: [PATCH 038/348] . (#9676) --- .../mindustry/entities/bullet/ContinuousFlameBulletType.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/entities/bullet/ContinuousFlameBulletType.java b/core/src/mindustry/entities/bullet/ContinuousFlameBulletType.java index 4d67083cec..30dd5011fb 100644 --- a/core/src/mindustry/entities/bullet/ContinuousFlameBulletType.java +++ b/core/src/mindustry/entities/bullet/ContinuousFlameBulletType.java @@ -49,6 +49,7 @@ public class ContinuousFlameBulletType extends ContinuousBulletType{ lifetime = 16f; hitColor = colors[1].cpy().a(1f); lightColor = hitColor; + lightOpacity = 0.7f; laserAbsorb = false; ammoMultiplier = 1f; pierceArmor = true; @@ -87,7 +88,7 @@ public class ContinuousFlameBulletType extends ContinuousBulletType{ } Tmp.v1.trns(b.rotation(), realLength * 1.1f); - Drawf.light(b.x, b.y, b.x + Tmp.v1.x, b.y + Tmp.v1.y, lightStroke, lightColor, 0.7f); + Drawf.light(b.x, b.y, b.x + Tmp.v1.x, b.y + Tmp.v1.y, lightStroke, lightColor, lightOpacity); Draw.reset(); } From 138bbdb1c9d18408ae047f5d9f991a57ccab93b0 Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 28 Mar 2024 10:01:20 -0400 Subject: [PATCH 039/348] Dispose item pixmaps --- core/src/mindustry/type/Item.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/mindustry/type/Item.java b/core/src/mindustry/type/Item.java index f451b5f1a3..1c59b0d07a 100644 --- a/core/src/mindustry/type/Item.java +++ b/core/src/mindustry/type/Item.java @@ -146,6 +146,7 @@ public class Item extends UnlockableContent implements Senseable{ Pixmap res = Pixmaps.blend(pixmaps[i], pixmaps[(i + 1) % frames], f); packer.add(PageType.main, name + "-t" + index, res); + res.dispose(); } } } From 67e9e6be27a76d7bc546f2cd51e43de366564a51 Mon Sep 17 00:00:00 2001 From: PolarStar <107398572+PoIarStar@users.noreply.github.com> Date: Fri, 29 Mar 2024 21:58:44 +0300 Subject: [PATCH 040/348] Update servers_v7.json (#9680) Added attack server --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 7ef103cac1..1b49614a5f 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -34,7 +34,7 @@ }, { "name": "CMS Empire", - "address": ["trelesco.xyz"] + "address": ["trelesco.xyz", "95.84.198.97"] }, { "name": "ShardDustry Eventos", From 5a278c626172d04f5faac387f6970d4314d10d77 Mon Sep 17 00:00:00 2001 From: c6b2qb5g <160118416+c6b2qb5g@users.noreply.github.com> Date: Sat, 30 Mar 2024 01:58:50 +0700 Subject: [PATCH 041/348] Update servers_v7.json (#9683) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 1b49614a5f..49168d7da9 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -295,7 +295,7 @@ }, { "name": "NPTS", - "address": ["81.94.159.242"] + "address": ["77.87.215.102:26863"] }, { "name": "Skywar.VN", From 64d1a1f8cebda2e0afe37b76e5e20e35f3314ce4 Mon Sep 17 00:00:00 2001 From: GaviTSRA <61122293+GaviTSRA@users.noreply.github.com> Date: Fri, 29 Mar 2024 19:58:57 +0100 Subject: [PATCH 042/348] Update servers_v7.json - Add TSR Survival and TSR Attack (#9685) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 49168d7da9..8553f2187f 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -1,7 +1,7 @@ [ { "name": "TSR Network", - "address": ["de-prem-01.hosts.optikservers.com:35526"] + "address": ["de-prem-01.hosts.optikservers.com:35526", "de-prem-01.hosts.optikservers.com:35915", "de-prem-01.hosts.optikservers.com:35250"] }, { "name": "Toast Mindustry", From ee412c61fc120c9241a0ecd74dce72e0234eb3fd Mon Sep 17 00:00:00 2001 From: "nuri (smol)" <75618732+SMOLKEYS@users.noreply.github.com> Date: Sat, 30 Mar 2024 03:01:59 +0800 Subject: [PATCH 043/348] pierceFragCap (#9684) Co-authored-by: SMOLKEYS --- core/src/mindustry/entities/bullet/BulletType.java | 5 ++++- core/src/mindustry/entities/comp/BulletComp.java | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/entities/bullet/BulletType.java b/core/src/mindustry/entities/bullet/BulletType.java index 64737495e7..85bd903395 100644 --- a/core/src/mindustry/entities/bullet/BulletType.java +++ b/core/src/mindustry/entities/bullet/BulletType.java @@ -176,6 +176,8 @@ public class BulletType extends Content implements Cloneable{ public float fragLifeMin = 1f, fragLifeMax = 1f; /** Random offset of frag bullets from the parent bullet. */ public float fragOffsetMin = 1f, fragOffsetMax = 7f; + /** How many times this bullet can release frag bullets, if pierce = true. */ + public int pierceFragCap = -1; /** Bullet that is created at a fixed interval. */ public @Nullable BulletType intervalBullet; @@ -509,12 +511,13 @@ public class BulletType extends Content implements Cloneable{ } public void createFrags(Bullet b, float x, float y){ - if(fragBullet != null && (fragOnAbsorb || !b.absorbed)){ + if(fragBullet != null && (fragOnAbsorb || !b.absorbed) && !(b.frags >= pierceFragCap && pierceFragCap > 0)){ for(int i = 0; i < fragBullets; i++){ float len = Mathf.random(fragOffsetMin, fragOffsetMax); float a = b.rotation() + Mathf.range(fragRandomSpread / 2) + fragAngle + ((i - fragBullets/2) * fragSpread); fragBullet.create(b, x + Angles.trnsx(a, len), y + Angles.trnsy(a, len), a, Mathf.random(fragVelocityMin, fragVelocityMax), Mathf.random(fragLifeMin, fragLifeMax)); } + b.frags++; } } diff --git a/core/src/mindustry/entities/comp/BulletComp.java b/core/src/mindustry/entities/comp/BulletComp.java index 7483d66496..d9c3aef000 100644 --- a/core/src/mindustry/entities/comp/BulletComp.java +++ b/core/src/mindustry/entities/comp/BulletComp.java @@ -45,6 +45,7 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw transient @Nullable Mover mover; transient boolean absorbed, hit; transient @Nullable Trail trail; + transient int frags; @Override public void getCollisions(Cons consumer){ From 5d180e9dd8e83e0a7390a790a15e43c141a958b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Mesk=C3=B3?= Date: Fri, 29 Mar 2024 20:02:34 +0100 Subject: [PATCH 044/348] Update Hungarian translation (#9675) * Update Hungarian translation * Update bundle_hu.properties Changed triple dots to three dots * Further fixes * Done requested fixes * Arkycite terminology changed --- core/assets/bundles/bundle_hu.properties | 2298 +++++++++++----------- 1 file changed, 1149 insertions(+), 1149 deletions(-) diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index 21f84cf25c..3a372d6433 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -1,6 +1,6 @@ -credits.text = Készítette: [royal]Anuken[] - [sky]anukendev@gmail.com[] +credits.text = Készítette: [royal]Anuken[] – [sky]anukendev@gmail.com[] credits = Készítők -contributors = Közreműködők És Fordítók +contributors = Közreműködők és fordítók discord = Csatlakozz a Mindustry Discord szerverhez! link.discord.description = Az eredeti Mindustry Discord csevegőszoba link.reddit.description = A Mindustry subreddit @@ -8,14 +8,14 @@ link.github.description = A játék forráskódja link.changelog.description = Frissítési változások listája link.dev-builds.description = Instabil fejlesztői összeállítások link.trello.description = Hivatalos Trello tábla a tervezett funkcióknak -link.itch.io.description = itch.io oldal PC letöltésekkel -link.google-play.description = Google Play áruház listázás -link.f-droid.description = F-Droid katalógus listázás +link.itch.io.description = itch.io oldal PC-s letöltésekkel +link.google-play.description = Listázás a Google Play áruházban +link.f-droid.description = Listázás az F-Droidon link.wiki.description = Hivatalos Mindustry wiki link.suggestions.description = Új funkciók ajánlása -link.bug.description = Találtál egy szoftver hibát? Itt jelentheted -linkopen = Ez a kiszolgáló egy linket küldött neked. Biztos vagy benne, hogy megnyitod?\n\n[sky]{0} -linkfail = Nem sikerült megnyitni a linket!\nAz URL a vágólapra lett másolva. +link.bug.description = Találtál egy szoftverhibát? Itt jelentheted +linkopen = Ez a kiszolgáló egy hivatkozást küldött. Biztos vagy benne, hogy megnyitod?\n\n[sky]{0} +linkfail = Nem sikerült megnyitni a hivatkozást!\nA webcím a vágólapra lett másolva. screenshot = Képernyőkép mentve ide: {0} screenshot.invalid = Túl nagy a pálya, nincs elég memória a képernyőképhez. gameover = A játéknak vége @@ -41,12 +41,12 @@ be.ignore = Most nem be.noupdates = Nem található frissítés. be.check = Frissítések keresése -mods.browser = Mod választó +mods.browser = Modböngésző mods.browser.selected = Mod kiválasztása mods.browser.add = Letöltés mods.browser.reinstall = Újratelepítés mods.browser.view-releases = Kiadások megtekintése -mods.browser.noreleases = [scarlet]Nem találhatóak a kiadások\n[accent]Nem lehet kiadásokat találni ehhez a Modhoz. Nézd meg a tárolóját, hogy vannak-e kiadásai. +mods.browser.noreleases = [scarlet]Nem találhatóak a kiadások\n[accent]Nem találhatók kiadások ehhez a modhoz. Nézd meg a tárolóját, hogy vannak-e kiadásai. mods.browser.latest = [lightgray][Legújabb] mods.browser.releases = Kiadások mods.github.open = Tároló @@ -64,15 +64,15 @@ schematic.import = Vázlat importálása... schematic.exportfile = Exportálás fájlba schematic.importfile = Importálás fájlból schematic.browseworkshop = Steam Műhely megtekintése -schematic.copy = Másolás a vágólapra -schematic.copy.import = Importálás a vágólapról +schematic.copy = Végólapra másolás +schematic.copy.import = Importálás vágólapról schematic.shareworkshop = Megosztás a Steam Műhelyben schematic.flip = [accent][[{0}][]/[accent][[{1}][]: Vázlat tükrözése schematic.saved = Vázlat elmentve. schematic.delete.confirm = Ez a vázlat törölve lesz. schematic.edit = Vázlat szerkesztése schematic.info = {0}x{1}, {2} blokk -schematic.disabled = [scarlet]Vázlatok letiltva[]\nNem használhatsz vázlatokat ezen a [accent]pályán[], vagy [accent]kiszolgálón. +schematic.disabled = [scarlet]Vázlatok letiltva[]\nNem használhatsz vázlatokat ezen a [accent]pályán[] vagy [accent]kiszolgálón. schematic.tags = Címkék: schematic.edittags = Címkék szerkesztése schematic.addtag = Címke hozzáadása @@ -93,18 +93,18 @@ stats.deconstructed = Építmények lebontva stats.playtime = Játékban töltött idő globalitems = [accent]A bolygó nyersanyagai -map.delete = Biztosan törölni akarod a(z) "[accent]{0}[]" pályát? +map.delete = Biztosan törölni akarod a(z) „[accent]{0}[]” pályát? level.highscore = Legmagasabb pontszám: [accent]{0} level.select = Szint kiválasztása level.mode = Játékmód: -coreattack = < A Támaszpont támadás alatt van! > +coreattack = < A támaszpont támadás alatt van! > nearpoint = [[ [scarlet]AZONNAL HAGYD EL A LEDOBÁSI PONTOT![] ]\nA megsemmisülés fenyeget! -database = Támaszpont adatbázis +database = Támaszpont adatbázisa database.button = Adatbázis savegame = Játék mentése loadgame = Játék betöltése joingame = Kapcsolódás egy játékhoz -customgame = Egyedi játék +customgame = Egyéni játék newgame = Új játék none = none.found = [lightgray] @@ -129,15 +129,15 @@ committingchanges = Változások rögzítése done = Kész feature.unsupported = Ez az eszköz nem támogatja ezt a funkciót. -mods.initfailed = [red]⚠[] Az előző Mindustry munkamenet nem tudott inicializálódni. Ez valószínű egy rosszúl működő Mod miatt történt.\n\nAz "összeomlások" elkerülése érdekében, [red]minden Mod le lett tiltva.[] +mods.initfailed = [red]⚠[] Az előző Mindustry példány előkészítése nem sikerült. Ezt valószínűleg egy rosszul működő mod okozta.\n\nAz ismételt összeomlások elkerülése érdekében [red]minden mod le lett tiltva.[] mods = Modok -mods.none = [lightgray]Nem találhatóak Modok! -mods.guide = Mod készítési útmutató +mods.none = [lightgray]Nem találhatóak modok! +mods.guide = Modkészítési útmutató mods.report = Hiba jelentése mods.openfolder = Mappa megnyitása mods.viewcontent = Tartalom megtekintése mods.reload = Újratöltés -mods.reloadexit = A játék újraindul, hogy betöltődjenek a Modok. +mods.reloadexit = A játék most kilép, hogy újratöltse a modokat. mod.installed = [[Telepítve] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Aktív @@ -145,7 +145,7 @@ mod.disabled = [red]Inaktív mod.multiplayer.compatible = [gray]Többjátékos kompatibilis mod.disable = Letiltás mod.content = Tartalom: -mod.delete.error = Nem lehet törölni a Modot. Lehet, hogy egy másik folyamat használja. +mod.delete.error = Nem lehet törölni a modot. Lehet, hogy egy másik folyamat használja. mod.incompatiblegame = [red]Elavult játék mod.incompatiblemod = [red]Inkompatibilis @@ -155,48 +155,48 @@ mod.erroredcontent = [red]Hibás tartalom mod.circulardependencies = [red]Körkörös függőségek mod.incompletedependencies = [red]Hiányos függőségek -mod.requiresversion.details = Szükséges játékverzió: [accent]{0}[]\nA játék ezen verziója elavult! Ez a Mod egy újabb verziót kíván (valószínűleg egy Béta, vagy egy Alfa kiadást) a működéshez. -mod.outdatedv7.details = Ez a Mod nem kompatibilis a játék legújabb verziójával! A Mod készítőjének frissítenie kell azt és hozzá kell adnia a [accent]minGameVersion: 136[]-t a [accent]mod.json[] fájlhoz. -mod.blacklisted.details = Ez a Mod manuálisan a feketelistára került, mert a játék összeomlott tőle, vagy más probléma miatt. Ne használd! -mod.missingdependencies.details = Ez a Mod függőségeket hiányol: {0} -mod.erroredcontent.details = Ez a Mod hibákat okozott a betöltésnél. Kérd meg a Mod készítőjét, hogy javítsa ki a hibákat. -mod.circulardependencies.details = Ennek a Modnak egymástól függő függőségei vannak. -mod.incompletedependencies.details = Ez a Mod nem tudott betölteni a hiányzó, vagy a rossz függőségek miatt: {0}. +mod.requiresversion.details = Szükséges játékverzió: [accent]{0}[]\nA játék ezen verziója elavult! A mod működéséhez újabb verzió szükséges (valószínűleg egy béta vagy alfa kiadás). +mod.outdatedv7.details = Ez a mod nem kompatibilis a játék legújabb verziójával! A mod készítőjének frissítenie kell azt és hozzá kell adnia ezt a [accent]mod.json[] fájlhoz: [accent]minGameVersion: 136[]. +mod.blacklisted.details = Ez a mod kézileg feketelistára került, mert a játék összeomlott tőle, vagy más problémát okozott. Ne használd! +mod.missingdependencies.details = Ez a mod függőségeket hiányol: {0} +mod.erroredcontent.details = Ez a mod hibákat okozott a betöltésnél. Kérd meg a mod készítőjét, hogy javítsa őket. +mod.circulardependencies.details = Ennek a modnak egymástól függő függőségei vannak. +mod.incompletedependencies.details = Ez a mod nem tölthető be az érvénytelen vagy hiányzó függőségek miatt: {0}. mod.requiresversion = Szükséges játékverzió: [red]{0}[] mod.errors = Hiba történt a tartalom betöltése közben. -mod.noerrorplay = [red]Vannak hibás Modok.[] Kapcsold ki, vagy javítsd ki őket a játék előtt. -mod.nowdisabled = [red]A '{0}' Modnak nincs megfelelő függősége:[accent] {1}\n[lightgray]Ezeket előbb le kell tölteni.\nEz a Mod automatikusan törölve lesz. +mod.noerrorplay = [red]Hibákkal rendelkező modjaid vannak.[] Kapcsold ki, vagy javítsd ki őket a játék előtt. +mod.nowdisabled = [red]A(z) „{0}” modnak nincs megfelelő függősége:[accent] {1}\n[lightgray]Ezeket előbb le kell tölteni.\nEz a mod automatikusan ki lesz kapcsolva. mod.enable = Engedélyezés mod.requiresrestart = A játék kilép a módosítások alkalmazásához. -mod.reloadrequired = [red]Újratöltés szükséges +mod.reloadrequired = [red]Újraindítás szükséges mod.import = Mod importálása mod.import.file = Fájl importálása -mod.import.github = Importálás GitHub-ról -mod.jarwarn = [scarlet]A JAR Modok eredendően nem biztonságosak.[]\nGyőződj meg arról, hogy ezt a Modot megbízható forrásból importálod! -mod.item.remove = Ez az elem része a(z)[accent] '{0}'[] Modnak. A törléséhez távolítsd el a Modot. -mod.remove.confirm = Ez a Mod törölve lesz. +mod.import.github = Importálás GitHubról +mod.jarwarn = [scarlet]A JAR modok eredendően nem biztonságosak.[]\nGyőződj meg arról, hogy ezt a modot megbízható forrásból importálod! +mod.item.remove = Ez az elem a(z)[accent] „{0}”[] mod része. A törléséhez távolítsd el a modot. +mod.remove.confirm = Ez a mod törölve lesz. mod.author = [lightgray]Készítő:[] {0} -mod.missing = Ez a mentés nemrég törölt, vagy frissített Modokat tartalmaz. Elképzelhető, hogy nem fog működni. Biztosan betöltöd?\n[lightgray]Modok:\n{0} -mod.preview.missing = Mielőtt publikálod ezt a Modot a Steam Műhelyben, adj hozzá egy borítóképet.\nKészíts egy[accent] preview.png[] nevű képet a Mod mappájába, majd próbáld újra. -mod.folder.missing = Csak mappa formában lehet feltölteni a Steam Műhelybe.\nHogy átalakítsd, csomagold ki a ZIP fájlt egy mappába és töröld le a régit, majd indítsd újra a játékot, vagy töltsd újra a Modot. -mod.scripts.disable = Ez az eszköz nem támogatja a szkriptekkel rendelkező Modokat.\nA játékhoz tiltsd le ezeket a Modokat. +mod.missing = Ez a mentés nemrég törölt vagy frissített modokat tartalmaz. Elképzelhető, hogy nem fog működni. Biztosan betöltöd?\n[lightgray]Modok:\n{0} +mod.preview.missing = Mielőtt közzéteszed ezt a modot a Steam Műhelyben, adj hozzá egy borítóképet.\nKészíts egy[accent] preview.png[] nevű képet a Mod mappájába, majd próbáld újra. +mod.folder.missing = Csak mappa formában lehet feltölteni a Steam Műhelybe.\nHogy átalakítsd, bontsd ki a ZIP-fájlt egy mappába és töröld le a régit, majd indítsd újra a játékot, vagy töltsd újra a modokat. +mod.scripts.disable = Ez az eszköz nem támogatja a szkriptekkel rendelkező modokat.\nA játékhoz tiltsd le ezeket a modokat. -about.button = Rólunk +about.button = Névjegy name = Név: noname = Előbb válassz egy[accent] nevet[]. search = Keresés: -planetmap = Bolygó térkép +planetmap = Bolygótérkép launchcore = Támaszpont indítása -filename = Fájl név: -unlocked = Új tartalom kinyitva! -available = Új fejlesztés áll rendelkezésre! +filename = Fájlnév: +unlocked = Új tartalom feloldva! +available = Új fejlesztés érhető el! unlock.incampaign = < Oldd fel a hadjáratban a részletekért > campaign.select = Válassz kezdő hadjáratot campaign.none = [lightgray]Válassz egy bolygót a kezdéshez.\nEzt bármikor megváltoztathatod. -campaign.erekir = Újabb, csiszoltabb tartalom. Általában lineáris játékmenet.\n\nSokkal bonyolultabb. Magasabb minőségű pályák és élmények. -campaign.serpulo = Régebbi tartalom. A klasszikus élmények. Nyíltabb végű.\n\nPotenciálisan kiegyensúlyozatlan pályák és hadjárat. Kevésbé csiszolt. +campaign.erekir = Újabb, csiszoltabb tartalom. Többnyire lineáris játékmenet.\n\nSokkal nehezebb. Magasabb minőségű pályák és élmények. +campaign.serpulo = Régebbi tartalom. A klasszikus élmény. Nyíltabb végű, több tartalommal.\n\nPotenciálisan kiegyensúlyozatlan pályák és hadjárat. Kevésbé csiszolt. completed = [accent]Kész techtree = Fejlesztési fa techtree.select = Fejlesztési fa kiválasztása @@ -230,8 +230,8 @@ server.kicked.customClient = Ez a kiszolgáló nem támogatja a saját készít server.kicked.gameover = Vége a játéknak! server.kicked.serverRestarting = Ez a kiszolgáló újraindul. server.versions = A te játékverziód:[accent] {0}[]\nA kiszolgáló verziója:[accent] {1}[] -host.info = A [accent]Kiszolgáló indítása[] gomb egy kiszolgálót indít a [scarlet]6567[] porton.\nEzen a [lightgray] Wi-Fi-n, vagy a helyi hálózaton [] bárki láthatja a kiszolgálót a kiszolgálólistán.\n\nHa azt szeretnéd, hogy az emberek bárhonnan, IP-címmel kapcsolódhassanak, akkor [accent]porttovábbítás[] szükséges.\n\n[lightgray]Megjegyzés: ha valakinek problémái vannak a LAN-játékhoz való kapcsolódással, győződj meg arról, hogy a tűzfal beállításaiban engedélyezted-e a Mindustry hozzáférését a helyi hálózathoz. Ne feledd, hogy a nyilvános hálózatok néha nem teszik lehetővé a kiszolgálók felderítését. -join.info = Itt megadhatsz egy [accent]kiszolgáló IP[]-címet a kapcsolódáshoz, vagy felfedezhetsz [accent]helyi hálózati[], vagy [accent]globális[] kiszolgálókat a kapcsolódáshoz.\nA LAN és WAN többjátékos mód is támogatott.\n\n[lightgray]Ha valakihez IP alapján szeretnél kapcsolódni, akkor meg kell tudnod a partnered IP címét. Az IP-címeteket az interneten a "mi az IP-címem?" szövegre való kereséssel is megtalálhatjátok. +host.info = A [accent]kiszolgáló indítása[] gomb egy kiszolgálót indít a [scarlet]6567-es[] porton.\nEzen a [lightgray]Wi-Fi-n vagy a helyi hálózaton[] bárki láthatja a kiszolgálót a kiszolgálólistán.\n\nHa azt szeretnéd, hogy bárhonnan, IP-címmel kapcsolódhassanak, akkor [accent]porttovábbítás[] szükséges.\n\n[lightgray]Megjegyzés: ha valakinek problémái vannak a LAN-játékhoz való kapcsolódással, győződj meg arról, hogy a tűzfal beállításaiban engedélyezted-e a Mindustry hozzáférését a helyi hálózathoz. Ne feledd, hogy a nyilvános hálózatok néha nem teszik lehetővé a kiszolgálók felderítését. +join.info = Itt megadhatod egy [accent]kiszolgáló IP-címét[] a kapcsolódáshoz, vagy felfedezhetsz [accent]helyi[] vagy [accent]globális[] kiszolgálókat.\nA LAN és WAN többjátékos mód is támogatott.\n\n[lightgray]Ha valakihez IP-cím alapján szeretnél kapcsolódni, akkor meg kell tudnod az IP-címét, amelyet például a „my ip” webes kereséssel találhat meg. hostserver = Többjátékos játék invitefriends = Barátok meghívása hostserver.mobile = Többjátékos játék @@ -257,22 +257,22 @@ server.hidden = Rejtett viewplayer = Játékos figyelése: [accent]{0} trace = Játékos követése trace.playername = Játékos neve: [accent]{0} -trace.ip = IP: [accent]{0} +trace.ip = IP-cím: [accent]{0} trace.id = Azonosító: [accent]{0} trace.language = Nyelv: [accent]{0} trace.mobile = Mobil kliens: [accent]{0} trace.modclient = Nem hivatalos kliens: [accent]{0} trace.times.joined = Kapcsolódások száma: [accent]{0} trace.times.kicked = Kirúgások száma: [accent]{0} -trace.ips = IP-k: +trace.ips = IP-címek: trace.names = Nevek: -invalidid = Érvénytelen kliens azonosító (ID)! Küldj hibajelentést. +invalidid = Érvénytelen kliensazonosító (ID)! Küldj hibajelentést. player.ban = Kitiltás player.kick = Kirúgás player.trace = Követés -player.admin = Admin választás -player.team = Csapat váltás +player.admin = Admin be/ki +player.team = Csapatváltás server.bans = Tiltások server.bans.none = Nincsenek tiltott játékosok! @@ -285,31 +285,31 @@ server.outdated = [scarlet]Elavult kiszolgáló![] server.outdated.client = [scarlet]Elavult kliens![] server.version = [gray]v{0} {1} server.custombuild = [accent]Saját összeállítás -confirmban = Biztosan tiltod "{0}[white]" játékost? -confirmkick = Biztosan kirúgod "{0}[white]" játékost? +confirmban = Biztosan tiltod „{0}[white]” játékost? +confirmkick = Biztosan kirúgod „{0}[white]” játékost? confirmunban = Biztosan újra engedélyezed ezt a játékost? -confirmadmin = Biztosan előlépteted "{0}[white]" játékost adminná? -confirmunadmin = Biztosan meg akarod szüntetni "{0}[white]" játékosnak az adminisztrátori státuszát? +confirmadmin = Biztosan előlépteted „{0}[white]” játékost adminná? +confirmunadmin = Biztosan meg akarod szüntetni „{0}[white]” játékos adminisztrátori státuszát? votekick.reason = Kiszavazás oka -votekick.reason.message = Valóban kiakarod szavazni "{0}[white]"-t?\nHa igen, írd le miért: +votekick.reason.message = Valóban ki akarod szavazni „{0}[white]” játékost?\nHa igen, írd be az okát: joingame.title = Kapcsolódás a játékhoz joingame.ip = Cím: -disconnect = Lekapcsolódva. +disconnect = Kapcsolat bontva. disconnect.error = Kapcsolódási hiba. -disconnect.closed = Kapcsolat bontva. +disconnect.closed = Kapcsolat lezárva. disconnect.timeout = Időtúllépés. -disconnect.data = Nem sikerült betölteni a világ adatot! +disconnect.data = Nem sikerült betölteni a világ adatait! cantconnect = Nem sikerült kapcsolódni a(z) ([accent]{0}[]) játékhoz. connecting = [accent]Kapcsolódás... reconnecting = [accent]Újrakapcsolódás... -connecting.data = [accent]Világ adat betöltése... +connecting.data = [accent]Világadatok betöltése... server.port = Port: server.addressinuse = A cím már használatban van! server.invalidport = Érvénytelen port! -server.error = [scarlet]Kiszolgáló hiba. +server.error = [scarlet]Kiszolgálási hiba. save.new = Új mentés save.overwrite = Biztosan felülírod\nezt a mentést? -save.nocampaign = A hadjáratból származó egyes mentési fájlok nem importálhatóak. +save.nocampaign = A hadjáratból származó egyes mentési fájlok nem importálhatók. overwrite = Felülírás save.none = Nem található mentés! savefail = Nem sikerült elmenteni a játékot! @@ -324,7 +324,7 @@ save.newslot = Mentés neve: save.rename = Átnevezés save.rename.text = Új név: selectslot = Válassz ki egy mentést. -slot = [accent]Rekesz {0} +slot = [accent]{0}. hely editmessage = Üzenet szerkesztése save.corrupted = A mentési fájl sérült vagy érvénytelen! empty = <üres> @@ -335,19 +335,19 @@ save.autosave = Automatikus mentés: {0} save.map = Térkép: {0} save.wave = Hullám: {0} save.mode = Játékmód: {0} -save.date = Utolsó Mentés: {0} +save.date = Utolsó mentés: {0} save.playtime = Játékidő: {0} warning = Figyelmeztetés. confirm = Megerősítés delete = Törlés view.workshop = Megtekintés a Steam Műhelyben -workshop.listing = Steam Műhely listázás szerkesztése +workshop.listing = Steam Műhely listázásának szerkesztése ok = OK open = Megnyitás customize = Szabályok módosítása -cancel = Mégsem +cancel = Mégse command = Parancs -command.queue = Sorba állít +command.queue = Sorba állítás command.mine = Bányászás command.repair = Javítás command.rebuild = Újraépítés @@ -363,9 +363,9 @@ stance.shoot = Viselkedés: lövés stance.holdfire = Viselkedés: tüzet szüntess stance.pursuetarget = Viselkedés: célpont követése stance.patrol = Viselkedés: járőrözési útvonal -stance.ram = Viselkedés: ütközés\n[lightgray]Egyenes vonalú mozgás, nincs útkeresés -openlink = Link megnyitása -copylink = Link másolása +stance.ram = Viselkedés: ütközés\n[lightgray]Egyenes vonalú mozgás, útkeresés nélkül +openlink = Hivatkozás megnyitása +copylink = Hivatkozás másolása back = Vissza max = Max objective = A pálya célja @@ -376,18 +376,18 @@ data.export = Adatok exportálása data.import = Adatok importálása data.openfolder = Adatok mappájának megnyitása data.exported = Adatok exportálva. -data.invalid = Ez nem egy érvényes játékadat. +data.invalid = Ezek nem érvényes játékadatok. data.import.confirm = A külső adatok importálása felülírja[scarlet] minden[] jelenlegi játékadatodat.\n[accent]Nem vonható vissza![]\n\nAmint kész az importálás, a játék azonnal kilép. quit.confirm = Biztosan kilépsz? loading = [accent]Betöltés... downloading = [accent]Letöltés... saving = [accent]Mentés... -respawn = Nyomd meg a(z) [accent][[{0}][] gombot, hogy drónként újraéledj. -cancelbuilding = Használd a(z) [accent][[{0}][] gombot, hogy töröld a tervrajzot. -selectschematic = Használd a(z) [accent][[{0}][] gombot, hogy kijelölj és másolj. -pausebuilding = Használd a(z) [accent][[{0}][] gombot, hogy megállítsd az építkezést. -resumebuilding = Használd a(z) [scarlet][[{0}][] gombot, hogy folytasd az építkezést. -enablebuilding = Használd a(z) [scarlet][[{0}][] gombot, hogy jóváhagyd az építkezést. +respawn = [accent][[{0}][] az újraéledéshez +cancelbuilding = [accent][[{0}][] a tervrajz törléséhez +selectschematic = [accent][[{0}][] a kijelöléshez és másoláshoz +pausebuilding = [accent][[{0}][] az építkezés megállításához +resumebuilding = [scarlet][[{0}][] az építkezés folytatásához +enablebuilding = [scarlet][[{0}][] az építkezés jóváhagyásához showui = A kezelőfelület elrejtve.\nNyomd meg a(z) [accent][[{0}][] gombot a kezelőfelület megjelenítéséhez. commandmode.name = [accent]Parancs mód commandmode.nounits = [nincs egység] @@ -397,52 +397,52 @@ wave.waiting = [lightgray]A következő hullám elkezdődik: {0} mp múlva wave.waveInProgress = [lightgray]Hullám folyamatban waiting = [lightgray]Várakozás... waiting.players = Várakozás a játékosokra... -wave.enemies = [lightgray]{0}db ellenség maradt -wave.enemycores = [accent]{0}db[lightgray] ellenséges Támaszpont -wave.enemycore = [accent]{0}db[lightgray] ellenséges Támaszpont -wave.enemy = [lightgray]{0}db ellenség maradt +wave.enemies = [lightgray]{0} ellenség maradt +wave.enemycores = [accent]{0}[lightgray] ellenséges támaszpont +wave.enemycore = [accent]{0}[lightgray] ellenséges támaszpont +wave.enemy = [lightgray]{0} ellenség maradt wave.guardianwarn = Egy Őrző érkezik [accent]{0}[] hullám múlva. wave.guardianwarn.one = Egy Őrző érkezik [accent]{0}[] hullám múlva. loadimage = Kép betöltése saveimage = Kép mentése unknown = Ismeretlen -custom = Egyedi +custom = Egyéni builtin = Beépített map.delete.confirm = Biztosan törlöd ezt a pályát? Ez a művelet nem vonható vissza! map.random = [accent]Véletlenszerű pálya -map.nospawn = Ez a pálya nem rendelkezik Támaszponttal, amellyel a játékos kezdhet! Adj hozzá egy {0} Támaszpontot ehhez a pályához a szerkesztőben! -map.nospawn.pvp = Ezen a pályán nincs ellenséges Támaszpont, amellyel a másik csapat kezdhet! Adj hozzá egy [scarlet]nem narancssárga[] Támaszpontot ehhez a pályához a szerkesztőben! -map.nospawn.attack = Ezen a pályán nincs ellenséges Támaszpont! Adj hozzá egy {0} Támaszpontot ehhez a pályához a szerkesztőben! +map.nospawn = Ez a pálya nem rendelkezik támaszponttal, amellyel a játékos kezdhetne. Adj hozzá egy {0} támaszpontot a pályához a szerkesztőben. +map.nospawn.pvp = Ezen a pályán nincs ellenséges támaszpont, amellyel egy játékos kezdhet. Adj hozzá egy [scarlet]nem narancssárga[] támaszpontot a pályához a szerkesztőben. +map.nospawn.attack = Ezen a pályán nincs ellenséges támaszpont. Adj hozzá {0} támaszpontot ehhez a pályához a szerkesztőben. map.invalid = Hiba történt a pálya betöltésekor: sérült vagy érvénytelen fájl. workshop.update = Elem frissítése workshop.error = Hiba történt a Steam Műhely részleteinek lekérdezésekor: {0} -map.publish.confirm = Biztos, hogy közzéteszed ezt a pályát?\n\n[lightgray]Győződj meg róla, hogy elfogadtad a Steam Műhely EULA-t, különben a pályáid nem jelennek meg! +map.publish.confirm = Biztos, hogy közzéteszed ezt a pályát?\n\n[lightgray]Győződj meg róla, hogy elfogadtad a Steam Műhely EULA-t, különben a pályáid nem jelennek meg. workshop.menu = Válaszd ki, hogy mit szeretnél csinálni ezzel az elemmel. -workshop.info = Elem infó -changelog = Változásnapló (opcionális): +workshop.info = Eleminformációk +changelog = Változásnapló (nem kötelező): updatedesc = Cím és leírás felülírása eula = Steam EULA -missing = Ezt az elemet törölték vagy áthelyezték.\n[lightgray]A Steam Műhely adatait automatikusan leválasztották. -publishing = [accent]Publikálás... +missing = Ezt az elemet törölték vagy áthelyezték.\n[lightgray]A Steam Műhely adatai automatikusan le lettek választva. +publishing = [accent]Közzététel... publish.confirm = Biztosan közzéteszed?\n\n[lightgray]Győződj meg róla, hogy elfogadtad a Steam Műhely EULA-t, különben az elemeid nem jelennek meg! -publish.error = Hiba az elem publikálásakor: {0} -steam.error = Nem sikerült inicializálni a Steam szolgáltatásokat.\nHiba: {0} +publish.error = Hiba az elem közzétételekor: {0} +steam.error = Nem sikerült előkészíteni a Steam szolgáltatásokat.\nHiba: {0} editor.planet = Bolygó: editor.sector = Szektor: -editor.seed = Seed: +editor.seed = Kiindulóérték: editor.cliffs = Falak sziklákká editor.brush = Méret editor.openin = Megnyitás a szerkesztőben -editor.oregen = Érc generálás -editor.oregen.info = Érc generálás: -editor.mapinfo = Általános -editor.author = Készítő: +editor.oregen = Ércelőállítás +editor.oregen.info = Ércelőállítás: +editor.mapinfo = Pályainformációk +editor.author = Szerző: editor.description = Leírás: -editor.nodescription = A pályának rendelkeznie kell egy legalább 4 karakter hosszú leírással, mielőtt megosztod. -editor.waves = Hullámok: -editor.rules = Szabályok: -editor.generation = Generálás: +editor.nodescription = A megosztás előtt a pályának rendelkeznie kell egy legalább 4 karakteres leírással. +editor.waves = Hullámok +editor.rules = Szabályok +editor.generation = Előállítás editor.objectives = Célok editor.locales = Helyi csomagok editor.ingame = Szerkesztés a játékban @@ -453,9 +453,9 @@ editor.center = Ugrás középre editor.search = Pályák keresése... editor.filters = Pályák szűrése editor.filters.mode = Játékmódok: -editor.filters.type = Pálya típus: +editor.filters.type = Pályatípus: editor.filters.search = Keresés ebben: -editor.filters.author = Készítő +editor.filters.author = Szerző editor.filters.description = Leírás editor.shiftx = X eltolás editor.shifty = Y eltolás @@ -468,29 +468,29 @@ waves.health = élet: {0}% waves.perspawn = kezdőpontonként waves.shields = pajzs/hullám waves.to = - -waves.spawn = Kezdőpont: +waves.spawn = kezdőpont: waves.spawn.all = waves.spawn.select = Kezdőpont kiválasztása waves.spawn.none = [scarlet]nem találhatóak kezdőpontok a pályán -waves.max = max egységek +waves.max = egységkorlát waves.guardian = Őrző waves.preview = Előnézet waves.edit = Szerkesztés... -waves.random = Véletlen +waves.random = Véletlenszerű waves.copy = Másolás a vágólapra -waves.load = Másolás a vágólapról -waves.invalid = Nem lehet beilleszteni a vágólapról. +waves.load = Betöltés a vágólapról +waves.invalid = Érvénytelen hullámok a vágólapon. waves.copied = Hullámok másolva. -waves.none = Nincs ellenség megadva.\nVedd figyelembe, hogy az üresen hagyott hullám elrendezések automatikusan lecserélődnek az alapértelmezett elrendezésre. +waves.none = Nincs ellenség megadva.\nVedd figyelembe, hogy az üresen hagyott hullám-elrendezések automatikusan le lesznek cserélve az alapértelmezett elrendezésre. waves.sort = Rendezési szempont waves.sort.reverse = Rendezés visszafelé -waves.sort.begin = Indul +waves.sort.begin = Kezdés waves.sort.health = Élet waves.sort.type = Típus waves.search = Hullám keresése... -waves.filter = Egység szűrő -waves.units.hide = Mindent elrejt -waves.units.show = Mindent megjelenít +waves.filter = Egységszűrő +waves.units.hide = Összes elrejtése +waves.units.show = Összes megjelenítése #these are intentionally in lower case wavemode.counts = típusokra bontva @@ -499,56 +499,56 @@ wavemode.health = életpontok editor.default = [lightgray] details = Részletek... -edit = Szerkesztés... +edit = Szerkesztés variables = Változók logic.globals = Beépített változók editor.name = Név: -editor.spawn = Egység megidézése +editor.spawn = Egység létrehozása editor.removeunit = Egység eltávolítása editor.teams = Csapatok editor.errorload = Hiba a fájl betöltése közben. editor.errorsave = Hiba a fájl mentése közben. editor.errorimage = Ez egy kép, nem pedig egy pálya. editor.errorlegacy = Ez a pálya túl régi és olyan régi pálya formátumot használ, amely már nem támogatott. -editor.errornot = Ez nem egy pálya-fájl. -editor.errorheader = Ez a pálya-fájl vagy érvénytelen, vagy sérült. +editor.errornot = Ez nem egy pályafájl. +editor.errorheader = Ez a pályafájl érvénytelen vagy sérült. editor.errorname = A pályának nincs neve. Mentést próbálsz betölteni? -editor.errorlocales = Hiba az érvénytelen Helyi Csomagok beolvasásakor. +editor.errorlocales = Hiba az érvénytelen helyi csomagok beolvasásakor. editor.update = Frissítés editor.randomize = Véletlenszerű editor.moveup = Mozgás felfelé editor.movedown = Mozgás lefelé editor.copy = Másolás -editor.apply = Alkalmazás -editor.generate = Generálás -editor.sectorgenerate = Szektor generálása +editor.apply = Alkalmaz +editor.generate = Előállítás +editor.sectorgenerate = Szektor előállítása editor.resize = Átméretezés editor.loadmap = Pálya betöltése editor.savemap = Pálya mentése editor.savechanges = [scarlet]Nem mentett módosításaid vannak!\n\n[]Szeretnéd elmenteni őket? editor.saved = Mentve! -editor.save.noname = A pályádnak nincs neve! Állíts be egyet a 'pálya infó' menüben. -editor.save.overwrite = A pályád felülír egy beépített pályát! Válassz egy másik nevet az 'pálya infó' menüben! -editor.import.exists = [scarlet]Nem lehet importálni:[] Már létezik a(z) "{0}" nevű beépített pálya! +editor.save.noname = A pályádnak nincs neve! Állíts be egyet a „pályainformációk” menüben. +editor.save.overwrite = A pályád felülír egy beépített pályát! Válassz egy másik nevet a „pályainformációk” menüben. +editor.import.exists = [scarlet]Nem lehet importálni:[] Már létezik „{0}” nevű beépített pálya! editor.import = Importálás... editor.importmap = Pálya importálása editor.importmap.description = Létező pálya importálása editor.importfile = Fájl importálása -editor.importfile.description = Egy külső pálya fájl importálása +editor.importfile.description = Egy külső pályafájl importálása editor.importimage = Képfájl importálása -editor.importimage.description = Egy külső pálya képfájl importálása +editor.importimage.description = Egy külső pályaképfájl importálása editor.export = Exportálás... editor.exportfile = Fájl exportálása -editor.exportfile.description = Exportálás egy pálya fájlba -editor.exportimage = Domborzat kép exportálása +editor.exportfile.description = Exportálás egy pályafájlba +editor.exportimage = Domborzati kép exportálása editor.exportimage.description = Csak alapvető domborzatot tartalmazó képfájl exportálása editor.loadimage = Domborzat importálása editor.saveimage = Domborzat exportálása -editor.unsaved = Biztos ki akarsz lépni?\n[scarlet]A nem mentett módosításaid elvesznek! +editor.unsaved = Biztos, hogy ki akarsz lépni?\n[scarlet]A nem mentett módosításaid elvesznek. editor.resizemap = Pálya átméretezése editor.mapname = Pálya neve: editor.overwrite = [accent]Vigyázz!\nEzzel felülírsz egy már létező pályát. -editor.overwrite.confirm = [scarlet]Vigyázz![] Ilyen nevű pálya már létezik:\n"[accent]{0}[]"\nBiztosan felülírod? +editor.overwrite.confirm = [scarlet]Vigyázz![] Ilyen nevű pálya már létezik. Biztosan felülírod?\n„[accent]{0}[]” editor.exists = Ilyen nevű pálya már létezik. editor.selectmap = Válassz ki egy pályát a betöltéshez: @@ -559,20 +559,20 @@ toolmode.replaceall.description = Az összes blokkot lecseréli a pályán. toolmode.orthogonal = Merőleges toolmode.orthogonal.description = Csak merőleges vonalakat rajzol. toolmode.square = Négyzet -toolmode.square.description = Négyzet ecset +toolmode.square.description = Négyzetes ecset. toolmode.eraseores = Ércradír toolmode.eraseores.description = Csak az érceket törli. -toolmode.fillteams = Csoportok Kitöltése -toolmode.fillteams.description = Töltse ki a csoportokat a blokkok helyett. -toolmode.fillerase = Kitöltés Törlése -toolmode.fillerase.description = Törölje az azonos típusú blokkokat. -toolmode.drawteams = Csoportok Rajzolása -toolmode.drawteams.description = Csoportok rajzolása blokkok helyett. +toolmode.fillteams = Csapatok kitöltése +toolmode.fillteams.description = Csapatok kitöltése a blokkok helyett. +toolmode.fillerase = Kitöltés törlése +toolmode.fillerase.description = Az azonos típusú blokkok törlése. +toolmode.drawteams = Csapatok rajzolása +toolmode.drawteams.description = Csapatok rajzolása blokkok helyett. #unused toolmode.underliquid = Folyadékok alá -toolmode.underliquid.description = Padlók rajzolása a folyadék blokkok alá. +toolmode.underliquid.description = Padlók rajzolása a folyadékblokkok alá. -filters.empty = [lightgray]Még nincs szűrő! Adj hozzá egyet a lenti gombra kattintva! +filters.empty = [lightgray]Még nincs szűrő! Adj hozzá egyet a lenti gombra kattintva. filter.distort = Torzítás filter.noise = Zaj @@ -584,14 +584,14 @@ filter.oremedian = Érc medián filter.blend = Vegyes filter.defaultores = Alapértelmezett ércek filter.ore = Érc -filter.rivernoise = Vízfolyás zaj +filter.rivernoise = Vízfolyás zaja filter.mirror = Tükrözés -filter.clear = Blokkok törlése -filter.option.ignore = Elutasít -filter.scatter = Szórás +filter.clear = Törlés +filter.option.ignore = Elutasítás +filter.scatter = Szétszórás filter.terrain = Domborzat -filter.option.scale = Méret +filter.option.scale = Méretezés filter.option.chance = Gyakoriság filter.option.mag = Magnitúdó filter.option.threshold = Küszöbérték @@ -604,7 +604,7 @@ filter.option.rotate = Forgatás filter.option.amount = Mennyiség filter.option.block = Blokk filter.option.floor = Talaj -filter.option.flooronto = Cél talaj +filter.option.flooronto = Céltalaj filter.option.target = Cél filter.option.replacement = Csere filter.option.wall = Fal @@ -612,24 +612,24 @@ filter.option.ore = Érc filter.option.floor2 = Másodlagos talaj filter.option.threshold2 = Másodlagos küszöbérték filter.option.radius = Sugár -filter.option.percentile = Arány +filter.option.percentile = Százalék -locales.info = Itt adhatsz hozzá különböző Helyi Csomagokat a pályádhoz. A Helyi Csomagok minden tulajdonságának van egy neve és egy értéke. Ezeket a tulajdonságokat a Világ Processzorok és a Célkitűzések azok neveivel használhatják. Támogatják a szövegformázást (a helyőrzőket az aktuális értékükkel helyettesítik).\n\n[cyan]Példa tulajdonság:\n[]name: [accent]időzítő[]\nvalue: [accent]Példa időzítő, hátralévő idő: {0}[]\n\n[cyan]Használat:\n[]Beállítás Célkitűzés szövegeként: [accent]@időzítő\n\n[]Írja be egy Világ Processzorba:\n[accent]localeprint "időzítő"\nformat time\n[gray](ahol az idő egy külön kiszámított változó) -locales.deletelocale = Biztos, hogy törölni akarod ezt a Helyi Csomagot? -locales.applytoall = Változások alkalmazása az összes Helyi Csomagra -locales.addtoother = Hozzáadás más Helyi Csomaghoz +locales.info = Itt adhatsz hozzá különböző nyelvi csomagokat a pályádhoz. A nyelvi csomagokban minden tulajdonságnak van egy neve és egy értéke. Ezeket a tulajdonságokat a világfeldolgozók és a célkitűzések is használhatják a saját neveikkel. Támogatják a szövegformázást (a helyőrzőket a tényleges értékükkel helyettesítik).\n\n[cyan]Példa tulajdonság:\n[]name: [accent]időzítő[]\nvalue: [accent]Példa időzítő, hátralévő idő: {0}[]\n\n[cyan]Használat:\n[]Beállítás célkitűzés szövegeként: [accent]@időzítő\n\n[]Írd be egy világfeldolgozóba:\n[accent]localeprint "időzítő"\nformat time\n[gray](ahol az idő egy külön számított változó) +locales.deletelocale = Biztos, hogy törölni akarod ezt a nyelvi csomagot? +locales.applytoall = Változások alkalmazása az összes nyelvi csomagra +locales.addtoother = Hozzáadás más nyelvi csomagokhoz locales.rollback = Visszaállítás az utoljára elfogadottra -locales.filter = Tulajdonság szűrő -locales.searchname = Név keresés... -locales.searchvalue = Érték keresés... -locales.searchlocale = Helyi csomag keresés... +locales.filter = Tulajdonságszűrő +locales.searchname = Név keresése... +locales.searchvalue = Érték keresése... +locales.searchlocale = Nyelvi csomag keresése... locales.byname = Név szerint locales.byvalue = Érték szerint -locales.showcorrect = Azon tulajdonságok megjelenítése, amelyek mindenhol egyedi értékekkel rendelkeznek és jelen vannak minden Helyi Csomagban. -locales.showmissing = Azon tulajdonságok megjelenítése, amelyek hiányoznak Bizonyos Helyi Csomagokból -locales.showsame = Azon tulajdonságok megjelenítése, amelyek azonos értékekkel rendelkeznek bizonyos Helyi Csomagokban -locales.viewproperty = Megtekintés minden Helyi Csomagban -locales.viewing = "{0}" tulajdonság megtekintése +locales.showcorrect = Azon tulajdonságok megjelenítése, amelyek mindenhol egyedi értékekkel rendelkeznek és jelen vannak minden nyelvi csomagban. +locales.showmissing = Azon tulajdonságok megjelenítése, amelyek hiányoznak egyes nyelvi csomagokból +locales.showsame = Azon tulajdonságok megjelenítése, amelyek azonos értékekkel rendelkeznek különböző nyelvi csomagokban +locales.viewproperty = Megtekintés minden nyelvi csomagban +locales.viewing = A(z) „{0}” tulajdonság megtekintése locales.addicon = Ikon hozzáadása width = Szélesség: @@ -640,27 +640,27 @@ campaign = Hadjárat load = Betöltés save = Mentés fps = FPS: {0} -ping = Ping: {0}ms +ping = Ping: {0} ms tps = TPS: {0} -memory = Mem: {0}MB -memory2 = Mem:\n {0}MB +\n {1}MB -language.restart = Indítsd újra a játékot, hogy betöltődjenek a nyelvi beállítások! +memory = Mem: {0} MB +memory2 = Mem:\n {0} MB +\n {1} MB +language.restart = Indítsd újra a játékot, hogy a nyelvi beállítások érvénybe lépjenek. settings = Beállítások tutorial = Oktatóanyag tutorial.retake = Oktatóanyag újrajátszása editor = Szerkesztő -mapeditor = Pálya szerkesztő +mapeditor = Pályaszerkesztő abandon = Feladás abandon.text = Ez a szektor és minden nyersanyaga az ellenség kezére kerül. locked = Lezárva complete = [lightgray]Feltételek: -requirement.wave = Juss el a {0}. hullámig a(z) {1} szektorban -requirement.core = Pusztítsd el az ellenséges Támaszpontot a(z) {0} szektorban +requirement.wave = Juss el a(z) {0}. hullámig a(z) {1} szektorban +requirement.core = Pusztítsd el az ellenséges támaszpontot a(z) {0} szektorban requirement.research = Fejleszd ki: {0} requirement.produce = Gyártsd le: {0} requirement.capture = Foglald el a(z) {0} szektort -requirement.onplanet = Szektor elfoglalása a(z) {0}-n +requirement.onplanet = Szektor elfoglalása a(z) {0} bolygón requirement.onsector = Landolj a(z) {0} szektorban launch.text = Indítás research.multiplayer = Csak a kiszolgáló fedezhet fel nyersanyagokat. @@ -671,10 +671,10 @@ configure = Rakomány szerkesztése objective.research.name = Fejlesztés objective.produce.name = Megszerzés objective.item.name = Nyersanyag megszerzése -objective.coreitem.name = Támaszpont nyersanyag -objective.buildcount.name = Építés számláló -objective.unitcount.name = Egység számláló -objective.destroyunits.name = Egység megsemmisítése +objective.coreitem.name = Támaszpont nyersanyaga +objective.buildcount.name = Építésszámláló +objective.unitcount.name = Egységszámláló +objective.destroyunits.name = Egységek megsemmisítése objective.timer.name = Időzítő objective.destroyblock.name = Blokk megsemmisítése objective.destroyblocks.name = Blokkok megsemmisítése @@ -697,18 +697,18 @@ objective.produce = [accent]Termelj:\n[]{0}[lightgray]{1} objective.destroyblock = [accent]Semmisítsd meg:\n[]{0}[lightgray]{1} objective.destroyblocks = [accent]Semmisítsd meg: [lightgray]{0}[white]/{1}\n{2}[lightgray]{3} objective.item = [accent]Termelj: [][lightgray]{0}[]/{1}\n{2}[lightgray]{3} -objective.coreitem = [accent]Szállítás a Támaszpontba:\n[][lightgray]{0}[]/{1}\n{2}[lightgray]{3} -objective.build = [accent]Építs: [][lightgray]{0}[]db\n{1}[lightgray]{2} -objective.buildunit = [accent]Gyárts egységeket: [][lightgray]{0}[]db\n{1}[lightgray]{2} -objective.destroyunits = [accent]Semmisíts meg: [][lightgray]{0}[]db Egységet +objective.coreitem = [accent]Szállítás a támaszpontba:\n[][lightgray]{0}[]/{1}\n{2}[lightgray]{3} +objective.build = [accent]Építs: [][lightgray]{0}[]\n{1}[lightgray]{2} +objective.buildunit = [accent]Gyárts egységeket: [][lightgray]{0}[]\n{1}[lightgray]{2} +objective.destroyunits = [accent]Semmisíts meg: [][lightgray]{0}[] egységet objective.enemiesapproaching = [accent]Ellenség érkezik: [lightgray]{0}[] mp múlva objective.enemyescelating = [accent]Az ellenséges gyártás fokozódik: [lightgray]{0}[] mp múlva objective.enemyairunits = [accent]Az ellenséges légi egységek gyártása elkezdődik: [lightgray]{0}[] mp múlva -objective.destroycore = [accent]Semmisítsd meg az ellenséges Támaszpontot +objective.destroycore = [accent]Semmisítsd meg az ellenséges támaszpontot objective.command = [accent]Irányítsd az egységeket objective.nuclearlaunch = [accent]⚠ Nukleáris kilövés észlelve: [lightgray]{0} -announce.nuclearstrike = [red]⚠ BEÉRKEZŐ NUKLEÁRIS CSAPÁS ⚠\n[lightgray]Azonnal építs tartalék Támaszpontokat! +announce.nuclearstrike = [red]⚠ BEÉRKEZŐ NUKLEÁRIS CSAPÁS ⚠\n[lightgray]Azonnal építs tartalék támaszpontokat! loadout = Rakomány resources = Nyersanyagok @@ -717,10 +717,10 @@ bannedblocks = Tiltott épületek objectives = Feladatok bannedunits = Tiltott egységek bannedunits.whitelist = Tiltott egységek fehérlistára -bannedblocks.whitelist = Tiltott épületek fehérlistára +bannedblocks.whitelist = Tiltott blokkok fehérlistára addall = Összes hozzáadása launch.from = Indítás a(z) [accent]{0} szektorból -launch.capacity = Nyersanyag kapacitás az indításhoz: [accent]{0} +launch.capacity = Nyersanyag-kapacitás az indításkor: [accent]{0} launch.destination = Úticél: {0} configure.invalid = A mennyiségnek 0 és {0} között kell lennie. add = Hozzáadás... @@ -729,13 +729,13 @@ guardian = Őrző connectfail = [scarlet]Kapcsolódási hiba:\n\n[accent]{0} error.unreachable = A kiszolgálót nem lehet elérni.\nBiztosan jól írtad be a címet? error.invalidaddress = Érvénytelen cím. -error.timedout = Időtúllépés!\nGyőződj meg róla, hogy a "porttovábbítás" be van kapcsolva a kiszolgáló gépen és a cím helyes! -error.mismatch = Csomaghiba:\nLehetséges kliens/kiszolgáló verzió eltérés.\nGyőződj meg róla, hogy te és a kiszolgáló is a Mindustry legfrissebb verzióját használjátok! +error.timedout = Időtúllépés!\nGyőződj meg róla, hogy a porttovábbítás be van kapcsolva a kiszolgálógépen, és a cím helyes! +error.mismatch = Csomaghiba:\nLehetséges kliens- vagy kiszolgálóverzió-eltérés.\nGyőződj meg róla, hogy te és a kiszolgáló is a Mindustry legfrissebb verzióját használjátok! error.alreadyconnected = Már kapcsolódva vagy. -error.mapnotfound = A pálya fájl nem található! +error.mapnotfound = A pályafájl nem található! error.io = Internet I/O hiba. -error.any = Ismeretlen internet hiba. -error.bloom = Nem sikerült inicializálni a bloom-ot.\nElőfordulhat, hogy a készülék nem támogatja. +error.any = Ismeretlen hálózati hiba. +error.bloom = A bloom hatás előkészítése nem sikerült.\nElőfordulhat, hogy az eszköz nem támogatja. weather.rain.name = Eső weather.snow.name = Hóesés @@ -743,8 +743,8 @@ weather.sandstorm.name = Homokvihar weather.sporestorm.name = Spóravihar weather.fog.name = Köd -campaign.playtime = \uf129 [lightgray]Szektor Játékidő: {0} -campaign.complete = [accent]Gratuláció.\n\nAz ellenség a(z) {0}-n legyőzve.\n[lightgray]Az utolsó szektor meghódítása megtörtént. +campaign.playtime =  [lightgray]Játékidő a szektorban: {0} +campaign.complete = [accent]Gratulálunk.\n\nAz ellenség legyőzve a(z) {0} bolygón.\n[lightgray]Az utolsó szektor meghódítása megtörtént. sectorlist = Szektorok sectorlist.attacked = {0} támadás alatt @@ -753,10 +753,10 @@ sectors.resources = Nyersanyagok: sectors.production = Termelés: sectors.export = Export: sectors.import = Import: -sectors.time = A szektorban eltöltött idő: +sectors.time = Idő: sectors.threat = Fenyegetés: sectors.wave = Hullám: -sectors.stored = Eltárolt nyersanyagok: +sectors.stored = Tárolt nyersanyagok: sectors.resume = Folytatás sectors.launch = Indítás sectors.select = Kiválasztás @@ -764,12 +764,12 @@ sectors.nonelaunch = [lightgray]semmi (nap) sectors.rename = Szektor átnevezése sectors.enemybase = [scarlet]Ellenséges bázis sectors.vulnerable = [scarlet]Sebezhető -sectors.underattack = [scarlet]Támadás alatt! [accent]{0}% sérült +sectors.underattack = [scarlet]Támadás alatt! [accent]{0}%-ban sérült sectors.underattack.nodamage = [scarlet]Nincs meghódítva sectors.survives = [accent]Túlél {0} hullámot sectors.go = Utazás -sector.abandon = Elhagy -sector.abandon.confirm = Ennek a szektornak a Támaszpontja(i) önmegsemmisítésre kerülnek.\nFolytatod? +sector.abandon = Elhagyás +sector.abandon.confirm = Ennek a szektornak a támaszpontjai önmegsemmisítésre kerülnek.\nFolytatod? sector.curcapture = A szektor elfoglalva sector.curlost = A szektor elveszett sector.missingresources = [scarlet]Nincs elég nyersanyag @@ -779,7 +779,7 @@ sector.capture = A(z) [accent]{0}[white] szektor elfoglalva! sector.capture.current = A szektor elfoglalva! sector.changeicon = Ikon módosítása sector.noswitch.title = A szektorváltás nem lehetséges -sector.noswitch = Nem válthatsz szektort, amíg egy meglévő szektor támadás alatt áll.\n\nSzektor: [accent]{0}[] a(z) [accent]{1}[]-n +sector.noswitch = Nem válthatsz szektort, amíg egy meglévő szektor támadás alatt áll.\n\nSzektor: [accent]{0}[] a(z) [accent]{1}[] bolygón sector.view = A szektor megtekintése threat.low = Alacsony @@ -794,43 +794,43 @@ planet.serpulo.name = Serpulo planet.erekir.name = Erekir planet.sun.name = Nap -sector.impact0078.name = Ütközet 0078 -sector.groundZero.name = Becsapódási Pont -sector.craters.name = A Kráterek -sector.frozenForest.name = Fagyott Erdő -sector.ruinousShores.name = Romos Partok -sector.stainedMountains.name = Foltos Hegyek -sector.desolateRift.name = Kietlen Hasadék -sector.nuclearComplex.name = Nukleáris Termelési Komplexum -sector.overgrowth.name = Túlburjánzott +sector.impact0078.name = 0078-as becsapódás +sector.groundZero.name = Becsapódási pont +sector.craters.name = A kráterek +sector.frozenForest.name = Fagyott erdő +sector.ruinousShores.name = Romos partok +sector.stainedMountains.name = Foltos hegyek +sector.desolateRift.name = Kietlen hasadék +sector.nuclearComplex.name = Nukleáris termelési komplexum +sector.overgrowth.name = Túlburjánzás sector.tarFields.name = Kátránymezők -sector.saltFlats.name = Sós Síkságok +sector.saltFlats.name = Sós síkságok sector.fungalPass.name = Gombahágó -sector.biomassFacility.name = Biomassza Szintézis Létesítmény -sector.windsweptIslands.name = Szélfútta Szigetek -sector.extractionOutpost.name = Kivonási Pont -sector.planetaryTerminal.name = Bolygó Körüli Indító Terminál +sector.biomassFacility.name = Biomassza szintetizáló létesítmény +sector.windsweptIslands.name = Szélfútta szigetek +sector.extractionOutpost.name = Kivonási helyőrség +sector.planetaryTerminal.name = Bolygó körüli indítóterminál sector.coastline.name = Partvonal -sector.navalFortress.name = Tengerészeti Erőd +sector.navalFortress.name = Tengerészeti erőd -sector.groundZero.description = Az ideális helyszín, hogy ismét belekezdjünk. Alacsony ellenséges fenyegetés. Némi nyersanyag.\nGyűjts annyi Rezet és Ólmot, amennyit csak tudsz.\nHaladj tovább. -sector.frozenForest.description = Még itt, a hegyekhez közel is elterjedtek a spórák. A fagypont alatti hőmérséklet nem tudja örökké fogva tartani őket.\n\nFedezd fel az elektromosság erejét! Építs Belső Égetésű Erőművet! Használj Foltozót! -sector.saltFlats.description = A sivatag peremén terül el a Sós Síkságok néven ismert síkság. Kevés nyersanyag található errefelé.\n\nAz ellenség egy raktárkomplexumot létesített itt. Pusztítsd el a Támaszpontot! Kő kövön ne maradjon! -sector.craters.description = Régen háborúk folytak ezen a helyen és csak egy kráter maradt utánuk. Szerencsédre a kráter évezredek alatt feltöltődött Vízzel így letudod hűteni vele a Fúróidat és Lövegeidet. Persze előtte használd a Homokot, hogy legyen üveged! -sector.ruinousShores.description = A romokon túl fekszik a vízpart. Egykor itt állt egy parti védelmi vonal. Mára nem sok maradt belőle. Csak a legegyszerűbb védelmi épületek maradtak sértetlenek, minden más csak törmelékként van jelen.\nFolytasd a terjeszkedést! Fedezd fel a régi technológiákat! -sector.stainedMountains.description = Mélyebben benn a szárazföldön fekszenek a hegyek, a spóráktól még érintetlenül.\nTermeld ki a bőséges Titán készleteket a körzetben. Tanuld meg felhasználni!.\n\nAz ellenség itt nagyobb létszámban van jelen. Ne hagyj nekik időt, hogy a legerősebb egységeiket hadba állíthassák! +sector.groundZero.description = Az ideális helyszín, hogy ismét belekezdjünk. Alacsony ellenséges fenyegetés. Kevés nyersanyag.\nGyűjts annyi rezet és ólmot, amennyit csak tudsz.\nHaladj tovább. +sector.frozenForest.description = Még itt, a hegyekhez közel is elterjedtek a spórák. A fagypont alatti hőmérséklet nem tudja örökké fogva tartani őket.\n\nFedezd fel az elektromosság erejét! Építs égetőerőműveket! Tanuld meg a foltozók használatát! +sector.saltFlats.description = A sivatag peremén terülnek el a Sós síkságok. Kevés nyersanyag található errefelé.\n\nAz ellenség egy raktárkomplexumot létesített itt. Pusztítsd el a támaszpontjukat! Kő kövön ne maradjon! +sector.craters.description = Víz gyűjt össze ebben a kráterben, amely régi háborúk emlékét őrzi. Szerezd vissza a területet. Gyűjts homokot! Olvassz üveget! Pumpálj vizet, hogy lehűtsd a fúróidat és lövegtornyaidat. +sector.ruinousShores.description = A pusztaság mögött a partvonal húzódik. Valaha ezen a helyen egy partvédelmi rendszer állt. Nem sok minden maradt belőle. Csak a legalapvetőbb védelmi szerkezetek maradtak érintetlenül, minden más csak törmelék lett.\nFolytasd a terjeszkedést! Fedezd fel újra a technológiát! +sector.stainedMountains.description = Mélyebben a szárazföldön fekszenek a hegyek, a spóráktól még érintetlenül.\nTermeld ki a bőséges titán készleteket a körzetben. Tanuld meg felhasználni!.\n\nAz ellenség itt nagyobb létszámban van jelen. Ne hagyj nekik időt, hogy a legerősebb egységeiket hadba állíthassák! sector.overgrowth.description = Ez a terület közelebb esik a spórák forrásához, a spórák már kinőtték.\nAz ellenség egy helyőrséget létesített itt. Építs Mace egységeket! Pusztítsd el a bázist! -sector.tarFields.description = Egy Olajtermelő övezet peremvidéke a hegyek és a sivatag között. Egy azon kevés szektorok közül, ahol még hasznosítható kátránykészletek találhatóak.\nBár a terület elhagyatott, veszélyes ellenséges erők fészkelnek a közelben. Ne becsüld alá őket!\n\n[lightgray]Fedezd fel az Olajfeldolgozási lehetőségeket, ha tudod! -sector.desolateRift.description = Egy extrém veszélyes zóna. Nyersanyagokban gazdag, de szűkös a hely. Magas kockázat. Hagyd el, amint lehet! Ne tévesszen meg a hosszú szünet az ellenség támadásai között! -sector.nuclearComplex.description = Egy néhai létesítmény a Tórium kitermelésére és feldolgozására, romokban.\n[lightgray]Fedezd fel a Tóriumot és sokrétű felhasználását!\n\nAz ellenség nagy létszámban van jelen, és folyamatosan megfigyelés alatt tartják a környéket. -sector.fungalPass.description = Átmenet a magas hegyek és a mélyebben fekvő, spórák uralta lapály között. Egy kisebb ellenséges megfigyelő állomás található itt.\nSemmisítsd meg!\nHasználj Dagger és Crawler egységeket! Pusztítsd el a két Támaszpontot! -sector.biomassFacility.description = A Spórák származási helye. Ebben a létesítményben fejlesztették ki őket és eredetileg itt került sor a gyártásukra.\nFedezd fel az itt található technológiákat! Használd a Spóra Kapszulákat üzemanyag- és Műanyagok gyártására!\n\n[lightgray]A létesítmény pusztulása nyomán a spórák elszabadultak és szétszóródtak a légkörben. A helyi ökoszisztémában semmi sem tudta felvenni a versenyt egy ennyire invazív életformával. -sector.windsweptIslands.description = Távolabb a partvonalon túl fekszik ez az elszigetelt szigetcsoport. A feljegyzések szerint egykor [accent]Műanyag[] gyártása zajlott itt.\n\nVerd vissza az ellenség vízi egységeit! Állíts fel egy bázist a szigeteken! Fedezd fel az itt talált gyárakat! -sector.extractionOutpost.description = Egy távoli ellenséges támaszpont, amelyet az ellenség azért épített, hogy nyersanyagokat juttasson el más szektorokba.\n\nA szektorok közötti szállítási technológia elengedhetetlen a további hódításhoz. Pusztítsd el a bázist. Fejleszd ki a Kilövő Állást. -sector.impact0078.description = Itt fekszenek a roncsai az első csillagközi űrhajónak, amely ebbe a csillagrendszerbe érkezett.\n\nMents ki a romok alól mindent amit csak tudsz! Fedezd fel az épen maradt technológiákat. -sector.planetaryTerminal.description = A végső célpont.\n\nEzen a vízparti bázison található egy olyan építmény, amely képes Támaszpontokat kilőni a közeli bolygókra. Folyamatosan őrzik.\n\nKészíts vízi egységeket! Ártalmatlanítsd az ellenséget amilyen gyorsan csak tudod! Találd meg a kilövőszerkezetet! +sector.tarFields.description = Egy olajtermelő övezet peremvidéke a hegyek és a sivatag között. Egy azon kevés szektorok közül, ahol még hasznosítható kátránykészletek találhatók.\nBár a terület elhagyatott, veszélyes ellenséges erők fészkelnek a közelben. Ne becsüld alá őket!\n\n[lightgray]Fedezd fel az olajfeldolgozási lehetőségeket, ha tudod! +sector.desolateRift.description = Egy extrém veszélyes zóna. Nyersanyagokban gazdag, de szűkös a hely. Magas a kockázat. Építsd szárazföldi és légvédelmet, amint csak tudsz. Ne tévesszen meg a hosszú szünet az ellenség támadásai között. +sector.nuclearComplex.description = Egy néhai tóriumkitermelő és feldolgozó létesítmény, romokban.\n[lightgray]Fedezd fel a tóriumot és a sokrétű felhasználását!\n\nAz ellenség nagy létszámban van jelen, és folyamatosan megfigyelés alatt tartják a környéket. +sector.fungalPass.description = Átmeneti terület a magas hegyek és a mélyebben fekvő, spórák uralta lapály között. Egy kisebb ellenséges megfigyelő állomás található itt.\nSemmisítsd meg!\nHasználj Dagger és Crawler egységeket! Pusztítsd el a két támaszpontot! +sector.biomassFacility.description = A spórák származási helye. Ebben a létesítményben fejlesztették ki őket, és eredetileg itt is gyártották őket.\nFedezd fel az itt található technológiákat. Tenyészd ki a spórákat üzemanyag és műanyagok gyártásához.\n\n[lightgray]A létesítmény pusztulása nyomán a spórák elszabadultak és szétszóródtak a légkörben. A helyi ökoszisztémában semmi sem tudta felvenni a versenyt egy ennyire invazív életformával. +sector.windsweptIslands.description = Távolabb, a partvonalon túl fekszik ez az elszigetelt szigetcsoport. A feljegyzések szerint egykor [accent]műanyagot[] gyártottak itt.\n\nVerd vissza az ellenség vízi egységeit! Állíts fel egy bázist a szigeteken! Fedezd fel az itt talált gyárakat! +sector.extractionOutpost.description = Egy távoli ellenséges támaszpont, amelyet az ellenség azért épített, hogy nyersanyagokat juttasson el más szektorokba.\n\nA szektorok közötti szállítási technológia elengedhetetlen a további hódításhoz. Pusztítsd el a bázist. Fejleszd ki a kilövőállást. +sector.impact0078.description = Itt nyugszanak az ebbe a csillagrendszerbe érkező első csillagközi űrhajó maradványai.\n\nMents ki a romok alól mindent amit csak tudsz. Fedezd fel az épen maradt technológiákat. +sector.planetaryTerminal.description = A végső célpont.\n\nEzen a vízparti bázison egy olyan építmény található, amely képes támaszpontokat kilőni a közeli bolygókra. Rendkívül jól őrzik.\n\nKészíts vízi egységeket! Ártalmatlanítsd az ellenséget, amilyen gyorsan csak tudod! Találd meg a kilövőszerkezetet! sector.coastline.description = Ezen a helyen egy haditengerészeti egység technológiájának maradványait azonosították. Verd vissza az ellenséges támadásokat, foglald el ezt a szektort, és szerezd meg a technológiát. -sector.navalFortress.description = Az ellenség bázist létesített egy távoli, természetesen-megerősített szigeten. Pusztítsd el ezt az előőrsöt. Szerezd meg a fejlett hadihajó-technológiájukat, és fejleszd ki te is. +sector.navalFortress.description = Az ellenség bázist létesített egy távoli, természetes erődítményes szigeten. Pusztítsd el ezt az előőrsöt. Szerezd meg a fejlett hadihajó-technológiájukat, és fejleszd ki te is. sector.onset.name = A Kezdet sector.aegis.name = Égisz @@ -851,31 +851,31 @@ sector.karst.name = Karszt sector.origin.name = Eredet sector.onset.description = Kezdd meg az Erekir meghódítását. Gyűjts nyersanyagokat, állíts elő egységeket, és kezdd el a technológiai fejlesztéseket. -sector.aegis.description = Ez a szektor Volfrám lelőhelyeket tartalmaz.\nFejleszd ki az [accent]Ütvefúró[]t, hogy ki tudd bányászni ezt a nyersanyagot és elpusztítani az ellenséges bázist a szektorban. -sector.lake.description = Az Ebben a szektorban lévő Salakos tó nagymértékben korlátozza az ütőképes egységek használatát. A lebegőegységek használata az egyetlen lehetőség.\nFejleszd ki a [accent]Repülőgép Gyár[]at és állíts elő egy [accent]Elude[] egységet, amilyen hamar csak lehet. +sector.aegis.description = Ez a szektor volfrám-lelőhelyeket tartalmaz.\nFejleszd ki az [accent]Ütvefúrót[], hogy ki tudd bányászni ezt a nyersanyagot, és pusztítsd el az ellenséges bázist a szektorban. +sector.lake.description = Az ebben a szektorban lévő salakos tó nagymértékben korlátozza a használható egységeket. A lebegőegységek használata az egyetlen lehetőség.\nFejleszd ki a [accent]Repülőgépgyárat[], és állíts elő egy [accent]Elude[] egységet, amilyen hamar csak lehet. sector.intersect.description = A letapogatások arra utalnak, hogy ezt a szektort a leszállás után hamarosan több oldalról is megtámadják.\nÁllítsd fel gyorsan a védelmedet, és terjeszkedj minél hamarabb.\n[accent]Mech[] egységekre lesz szükség a terület zord terepviszonyai miatt. -sector.atlas.description = Ez a szektor változatos terepet tartalmaz, és az ütőképes támadáshoz többféle egységre lesz szükség.\nAz itt felfedezett ellenséges bázisok némelyikén való átjutáshoz is szükség lehet továbbfejlesztett egységekre.\nFejleszd ki az [accent]Elektrolizátor[]t és a [accent]Tank Újratervező[]t. +sector.atlas.description = Ez a szektor változatos terepet tartalmaz, és az ütőképes támadáshoz többféle egységre lesz szükség.\nAz itt felfedezett ellenséges bázisok némelyikén való átjutáshoz is továbbfejlesztett egységekre lehet szükség.\nFejleszd ki az [accent]Elektrolizátort[] és a [accent]Tank újratervezőt[]. sector.split.description = A minimális ellenséges jelenlét miatt ez a szektor tökéletes az új nyersanyagszállító technológiák tesztelésére. -sector.basin.description = Jelentős ellenséges jelenlét lett érzékelve ebben a szektorban.\nÉpíts gyorsan egységeket, és foglalj el ellenséges Támaszpontokat, hogy megvethesd a lábad. -sector.marsh.description = Ebben a szektorban rengeteg Arkycit található, de kevés a Víznyelő.\nÉpíts [accent]Kémiai Égető Kamrá[]t az áramfejlesztéshez. +sector.basin.description = Jelentős ellenséges jelenlét lett érzékelve ebben a szektorban.\nÉpíts gyorsan egységeket, és foglald el az ellenséges támaszpontokat, hogy megvethesd a lábad. +sector.marsh.description = Ebben a szektorban rengeteg arkicit található, de kevés a kürtő.\nÉpíts [accent]Kémiai égetőkamrát[] az áramfejlesztéshez. sector.peaks.description = A hegyvidéki terep ebben a szektorban a legtöbb egységet használhatatlanná teszi. Repülő egységekre lesz szükség.\nVigyázz az ellenséges légvédelmi létesítményekkel. Lehetséges, hogy az ilyen létesítményeket hatástalanítani lehet a támogató épületeik célba vételével. -sector.ravine.description = A szektorban nincs észlelve ellenséges Támaszpont, bár ez egy fontos szállítási útvonal az ellenség számára. Várhatóan változatos ellenséges erőkkel kell számolni. Gyárts [accent]Elektrometál[]t. Építs [accent]Afflict[] lövegtornyokat. +sector.ravine.description = A szektorban nem észlelhető ellenséges támaszpont, de ez egy fontos szállítási útvonal az ellenség számára, így változatos ellenséges erőkkel kell számolni.\nTermelj [accent]elektrometált[]. Építs [accent]Afflict[] lövegtornyokat. sector.caldera-erekir.description = Ebben a szektorban a feltárható nyersanyagok több szigeten szétszóródva találhatóak.\nFejleszd ki és helyezd üzembe a drónalapú szállítmányozást. -sector.stronghold.description = A nagy ellenséges tábor ebben a szektorban jelentős [accent]Tórium[] készleteket őriz.\nHasználd magasabb szintű egységek és lövegtornyok fejlesztésére. -sector.crevice.description = Ebben a szektorban az ellenség kegyetlen támadóerőket fog mozgósítani, hogy kiiktassa a bázisodat.\nGyűjts [accent]Karbid[]ot, majd fejleszd ki és építs [accent]Pirolízis Erőmű[]vet, mert lehet, hogy nélkülözhetetlenek lesznek a túléléshez. -sector.siege.description = Ebben a szektorban két párhuzamos kanyon található, amelyek két irányból érkező támadásokat tesznek lehetővé.\nFejleszd ki a [accent]Cianogén[]t, hogy még erősebb tankegységeket hozhass létre.\nVigyázat: Ellenséges, nagy hatótávolságú rakéták észlelve. A rakéták a becsapódásuk előtt megsemmisíthetők. -sector.crossroads.description = Az ellenséges támaszpontok ebben a szektorban változó terepviszonyok között alakultak ki. Ahhoz, hogy alkalmazkodni tudj kutass különböző egységek után.\nEzenkívül egyes bázisokat pajzsok védenek. Találd ki, hogyan táplálják őket. -sector.karst.description = Ez a szektor gazdag a nyersanyagokban, de amint egy új Támaszpont leszáll, az ellenség megtámadja azt.\nHasználd ki a nyersanyagokat és fedezd fel a [accent]Tóritkvarc[]ot. -sector.origin.description = Az utolsó szektor, jelentős ellenséges jelenléttel.\nNem valószínű, hogy maradt további fejlesztési lehetőség - koncentrálj az ellenséges Támaszpontok elpusztítására. +sector.stronghold.description = A nagy ellenséges tábor ebben a szektorban jelentős mennyiségű [accent]tóriumot[] őriz.\nHasználd magasabb szintű egységek és lövegtornyok fejlesztésére. +sector.crevice.description = Ebben a szektorban az ellenség kegyetlen támadóerőket fog mozgósítani, hogy kiiktassa a bázisodat.\nA [accent]karbid[] és a [accent]Pirolízis erőmű[] kifejlesztése nélkülözhetetlen lehet a túléléshez. +sector.siege.description = Ebben a szektorban két párhuzamos kanyon található, amelyek két irányból érkező támadásokat tesznek lehetővé.\nFejleszd ki a [accent]diciánt[], hogy még erősebb tankegységeket hozhass létre.\nVigyázat: ellenséges, nagy hatótávolságú rakéták észlelve. A rakéták a becsapódásuk előtt megsemmisíthetők. +sector.crossroads.description = Az ellenséges támaszpontok ebben a szektorban változó terepviszonyok között alakultak ki. Ahhoz, hogy alkalmazkodni tudj, fejlessz ki különböző egységeket.\nEzenkívül egyes bázisokat pajzsok védenek. Találd ki, hogyan táplálják őket. +sector.karst.description = Ez a szektor gazdag a nyersanyagokban, de amint egy új támaszpont leszáll, az ellenség megtámadja azt.\nHasználd ki a nyersanyagokat és fedezd fel a [accent]tóritkvarcot[]. +sector.origin.description = Az utolsó szektor, jelentős ellenséges jelenléttel.\nNem valószínű, hogy további fejlesztési lehetőségek maradtak – koncentrálj az ellenséges támaszpontok elpusztítására. status.burning.name = Égő -status.freezing.name = Fagyasztó +status.freezing.name = Fagyos status.wet.name = Nedves status.muddy.name = Sáros status.melting.name = Olvadó status.sapped.name = Kiszáradt status.electrified.name = Elektromos -status.spore-slowed.name = Spóra lassított +status.spore-slowed.name = Spórával lassított status.tarred.name = Kátrányozott status.overdrive.name = Túlhajtás status.overclock.name = Túlhúzás @@ -887,28 +887,28 @@ status.boss.name = Őrző settings.language = Nyelvek settings.data = Játékadatok settings.reset = Alapértelmezett -settings.rebind = Újrakötés -settings.resetKey = Visszaállítás +settings.rebind = Átállítás +settings.resetKey = Vissza-\nállítás settings.controls = Irányítás settings.game = Játék settings.sound = Hangok settings.graphics = Grafika settings.cleardata = Játékadatok törlése... settings.clear.confirm = Biztosan törlöd ezeket az adatokat?\nA műveletet nem lehet visszavonni! -settings.clearall.confirm = [scarlet] FIGYELEM! []\nEz törli az összes adatot, beleértve a mentéseket, pályákat, felfedezéseket és a billentyű beállításokat.\nAz 'OK' gomb megnyomásával a játék minden adatot töröl és automatikusan kilép. +settings.clearall.confirm = [scarlet]FIGYELEM![]\nEz törli az összes adatot, beleértve a mentéseket, pályákat, felfedezéseket és a billentyűbeállításokat.\nAz „OK” gomb megnyomásával a játék minden adatot töröl, és automatikusan kilép. settings.clearsaves.confirm = Biztosan törlöd az összes mentést? settings.clearsaves = Mentések törlése settings.clearresearch = Fejlesztések törlése settings.clearresearch.confirm = Biztosan törlöd az összes fejlesztést? -settings.clearcampaignsaves = Hadjárat mentések törlése -settings.clearcampaignsaves.confirm = Biztosan törlöd az összes hadjárat mentést? -paused = [accent]< Megállítva > +settings.clearcampaignsaves = Hadjáratmentések törlése +settings.clearcampaignsaves.confirm = Biztosan törlöd az összes hadjáratmentést? +paused = [accent]< Szünet > clear = Törlés banned = [scarlet]Kitiltva unsupported.environment = [scarlet]Nem támogatott környezet yes = Igen no = Nem -info.title = Infó +info.title = Információ error.title = [scarlet]Hiba történt error.crashtitle = Hiba történt unit.nobuild = [scarlet]Az egység nem tud építeni @@ -924,9 +924,9 @@ stat.maxefficiency = Maximális hatékonyság stat.booster = Erősítő stat.tiles = Szükséges talaj stat.affinities = Módosító körülmények -stat.opposites = Eltérések +stat.opposites = Ellentettek stat.powercapacity = Maximális tárolási kapacitás -stat.powershot = Áram/Lövés +stat.powershot = Áram/lövés stat.damage = Sebzés stat.targetsair = Repülő célpontok stat.targetsground = Földi célpontok @@ -935,39 +935,39 @@ stat.launchtime = Kilövések közti idő stat.shootrange = Hatótáv stat.size = Méret stat.displaysize = Felbontás -stat.liquidcapacity = Folyadék kapacitás -stat.powerrange = Áram hatótáv -stat.linkrange = Kapcsolat hatótáv -stat.instructions = Instrukciók -stat.powerconnections = Maximális kapcsolat +stat.liquidcapacity = Folyadékkapacitás +stat.powerrange = Áram hatótávja +stat.linkrange = Kapcsolat hatótávja +stat.instructions = Utasítások +stat.powerconnections = Max. kapcsolatok stat.poweruse = Áramhasználat -stat.powerdamage = Áram/Sebzés -stat.itemcapacity = Nyersanyag kapacitás -stat.memorycapacity = Memória méret +stat.powerdamage = Áram/sebzés +stat.itemcapacity = Nyersanyag-kapacitás +stat.memorycapacity = Memóriakapacitás stat.basepowergeneration = Alap áramtermelés stat.productiontime = Gyártási idő stat.repairtime = Teljes javítási idő stat.repairspeed = Javítási sebesség stat.weapons = Fegyverek -stat.bullet = Töltény +stat.bullet = Lövedék stat.moduletier = Modul szintje -stat.unittype = Egység típus +stat.unittype = Egység típusa stat.speedincrease = Gyorsítás stat.range = Hatótáv -stat.drilltier = Kitermelhető +stat.drilltier = Kitermelhetők stat.drillspeed = Alap termelési sebesség stat.boosteffect = Erősítés hatása -stat.maxunits = Maximális aktív egység +stat.maxunits = Max. aktív egységek stat.health = Életpontok stat.armor = Páncél stat.buildtime = Építési időtartam -stat.maxconsecutive = Maximum egymást követő +stat.maxconsecutive = Max. egymást követő stat.buildcost = Építési költség stat.inaccuracy = Pontatlanság stat.shots = Lövések stat.reload = Tüzelési sebesség stat.ammo = Lőszer -stat.shieldhealth = Pajzs élete +stat.shieldhealth = Pajzs életereje stat.cooldowntime = Újratöltés időtartama stat.explosiveness = Robbanékonyság stat.basedeflectchance = Alap hárítási esély @@ -983,35 +983,35 @@ stat.speed = Sebesség stat.buildspeed = Építési sebesség stat.minespeed = Termelési sebesség stat.minetier = Termelési szint -stat.payloadcapacity = Rakomány kapacitás +stat.payloadcapacity = Rakománykapacitás stat.abilities = Képességek stat.canboost = Erősíthető stat.flying = Repül -stat.ammouse = Lőszer használat -stat.damagemultiplier = Sebzés szorzó -stat.healthmultiplier = Életerő szorzó -stat.speedmultiplier = Sebesség szorzó -stat.reloadmultiplier = Újratöltés szorzó -stat.buildspeedmultiplier = Építési sebesség szorzó +stat.ammouse = Lőszerhasználat +stat.damagemultiplier = Sebzésszorzó +stat.healthmultiplier = Életerőszorzó +stat.speedmultiplier = Sebességszorzó +stat.reloadmultiplier = Újratöltési szorzó +stat.buildspeedmultiplier = Építési sebességszorzó stat.reactive = Reakciók stat.immunities = Immunitások stat.healing = Gyógyulás ability.forcefield = Erőtér ability.repairfield = Javító mező -ability.statusfield = Állapot mező +ability.statusfield = Állapotmező ability.unitspawn = Gyár -ability.shieldregenfield = Pajzs regeneráló mező +ability.shieldregenfield = Pajzsregeneráló mező ability.movelightning = Villámcsapás ability.shieldarc = Pajzs ív -ability.suppressionfield = Javítás elnyomás -ability.energyfield = Energia mező +ability.suppressionfield = Javítás elnyomása +ability.energyfield = Energiamező ability.energyfield.sametypehealmultiplier = [lightgray]Azonos típusú gyógyítás: [white]{0}% ability.energyfield.maxtargets = [lightgray]Célpontok maximális száma: [white]{0} ability.regen = Regeneráció -bar.onlycoredeposit = Csak a Támaszpont elhelyezése megengedett -bar.drilltierreq = Erősebb Fúró/Vágó szükséges +bar.onlycoredeposit = Csak a támaszpont elhelyezése megengedett +bar.drilltierreq = Erősebb fúró szükséges bar.noresources = Hiányzó nyersanyagok bar.corereq = Támaszpont szükséges bar.corefloor = Támaszpont zónacsempe szükséges @@ -1036,42 +1036,42 @@ bar.heatpercent = Hő: {0} ({1}%) bar.power = Áram bar.progress = Építés állapota bar.loadprogress = Állapot -bar.launchcooldown = Lehűlés indítása +bar.launchcooldown = Kilövés visszaszámlálása bar.input = Bemenet bar.output = Kimenet bar.strength = [stat]{0}[lightgray]x erő -units.processorcontrol = [lightgray]Processzor vezérelt +units.processorcontrol = [lightgray]Processzorvezérelt bullet.damage = [stat]{0}[lightgray] sebzés bullet.splashdamage = [stat]{0}[lightgray] területi sebzés ~[stat] {1}[lightgray] csempe bullet.incendiary = [stat]gyújtó bullet.homing = [stat]nyomkövető bullet.armorpierce = [stat]páncéltörő -bullet.maxdamagefraction = [stat]{0}%[lightgray] sebzés határérték -bullet.suppression = [stat]{0} mp[lightgray] javítás elnyomás ~ [stat]{1}[lightgray] csempe -bullet.interval = [stat]{0}/mp[lightgray] időszakos töltény(ek): -bullet.frags = [stat]{0}[lightgray]x repesz lövedék(ek): -bullet.lightning = [stat]{0}[lightgray]x villámcsapás ~ [stat]{1}[lightgray] sebzés -bullet.buildingdamage = [stat]{0}%[lightgray] épület sebzés +bullet.maxdamagefraction = [stat]{0}%[lightgray] sebzési határérték +bullet.suppression = [stat]{0} mp[lightgray] javításelnyomás ~[stat]{1}[lightgray] csempe +bullet.interval = [stat]{0}/mp[lightgray] lövedékek időköze: +bullet.frags = [stat]{0}[lightgray]x repeszlövedék: +bullet.lightning = [stat]{0}[lightgray]x villámcsapás ~[stat]{1}[lightgray] sebzés +bullet.buildingdamage = [stat]{0}%[lightgray] épületsebzés bullet.knockback = [stat]{0}[lightgray] hátralökés bullet.pierce = [stat]{0}[lightgray]x átütő erő bullet.infinitepierce = [stat]átütő erő -bullet.healpercent = [stat]{0}[lightgray]% gyógyító -bullet.healamount = [stat]{0}[lightgray] közvetlen javító -bullet.multiplier = [stat]{0}[lightgray]x lövedék szorzó -bullet.reload = [stat]{0}[lightgray]x tüzelési sebesség -bullet.range = [stat]{0}[lightgray] csempe tartomány +bullet.healpercent = [stat]{0}%[lightgray] javítás +bullet.healamount = [stat]{0}[lightgray] közvetlen javítás +bullet.multiplier = [stat]{0}[lightgray]x lőszerszorzó +bullet.reload = [stat]{0}%[lightgray] tüzelési sebesség +bullet.range = [stat]{0}[lightgray] csempés hatótáv -unit.blocks = csempe +unit.blocks = blokk unit.blockssquared = blokk² -unit.powersecond = áram egység/mp +unit.powersecond = áramegység/mp unit.tilessecond = csempe/mp -unit.liquidsecond = folyadék egység/mp +unit.liquidsecond = folyadékegység/mp unit.itemssecond = nyersanyag/mp -unit.liquidunits = folyadék egység -unit.powerunits = áram egység -unit.heatunits = hő egység +unit.liquidunits = folyadékegység +unit.powerunits = áramegység +unit.heatunits = hőegység unit.degrees = fok unit.seconds = másodperc unit.minutes = perc @@ -1079,10 +1079,10 @@ unit.persecond = /mp unit.perminute = /perc unit.timesspeed = x sebesség unit.percent = % -unit.shieldhealth = pajzs állapot +unit.shieldhealth = pajzs életereje unit.items = nyersanyag unit.thousands = k -unit.millions = Mil +unit.millions = mil unit.billions = Mrd unit.pershot = /lövés category.purpose = Cél @@ -1090,33 +1090,33 @@ category.general = Általános category.power = Áram category.liquids = Folyadékok category.items = Nyersanyagok -category.crafting = Bemenet/Kimenet +category.crafting = Bemenet/kimenet category.function = Funkció -category.optional = Lehetséges erősítés -setting.skipcoreanimation.name = Támaszpont indítás/leszállás animáció kihagyása +category.optional = Lehetséges fejlesztések +setting.skipcoreanimation.name = Támaszpont indítási/leszállási animáció kihagyása setting.landscape.name = Fekvő mód zárolása setting.shadows.name = Árnyékok -setting.blockreplace.name = Automatikus blokk javaslatok +setting.blockreplace.name = Automatikus blokkjavaslatok setting.linear.name = Lineáris szűrés setting.hints.name = Tanácsok setting.logichints.name = Logikai tanácsok setting.backgroundpause.name = Szüneteltetés a háttérben setting.buildautopause.name = Automatikus szünet építéskor setting.doubletapmine.name = Bányászás dupla kattintással/koppintással -setting.commandmodehold.name = Tartsd lenyomva a parancs módhoz -setting.distinctcontrolgroups.name = Egységenként legfeljebb egy ellenőrző csoport -setting.modcrashdisable.name = Modok letiltása indítási összeomláskor +setting.commandmodehold.name = Lenyomva tartás a parancs módhoz +setting.distinctcontrolgroups.name = Egységenként legfeljebb egy vezérlőcsoport +setting.modcrashdisable.name = Modok letiltása indításkori összeomláskor setting.animatedwater.name = Animált felületek setting.animatedshields.name = Animált pajzsok -setting.playerindicators.name = Játékos mutató -setting.indicators.name = Ellenség mutató +setting.playerindicators.name = Játékosjelzők +setting.indicators.name = Ellenségjelzők setting.autotarget.name = Automatikus célzás setting.keyboard.name = Irányítás egérrel és billentyűzettel setting.touchscreen.name = Irányítás érintőképernyővel -setting.fpscap.name = Max FPS +setting.fpscap.name = FPS-korlát setting.fpscap.none = Nincs setting.fpscap.text = {0} FPS -setting.uiscale.name = UI méretezése +setting.uiscale.name = Felület méretezése setting.uiscale.description = A módosítások alkalmazásához újraindítás szükséges. setting.swapdiagonal.name = Mindig átlós elhelyezés setting.difficulty.training = Kiképzés @@ -1126,78 +1126,78 @@ setting.difficulty.hard = Nehéz setting.difficulty.insane = Őrült setting.difficulty.name = Nehézség: setting.screenshake.name = Képernyő rázkódása -setting.bloomintensity.name = Bloom intenzitás -setting.bloomblur.name = Bloom Blur +setting.bloomintensity.name = Bloom intenzitása +setting.bloomblur.name = Bloom elmosása setting.effects.name = Hatások megjelenítése -setting.destroyedblocks.name = Elpusztított épületek megjelenítése -setting.blockstatus.name = Épületek állapotának megjelenítése -setting.conveyorpathfinding.name = Szállítószalag útvonalkeresés építéskor -setting.sensitivity.name = Kontroller érzékenység +setting.destroyedblocks.name = Elpusztított blokkok megjelenítése +setting.blockstatus.name = Blokkok állapotának megjelenítése +setting.conveyorpathfinding.name = Szállítószalag útvonalkeresése építéskor +setting.sensitivity.name = Kontroller érzékenysége setting.saveinterval.name = Mentési időköz setting.seconds = {0} másodperc setting.milliseconds = {0} ezredmásodperc setting.fullscreen.name = Teljes képernyő setting.borderlesswindow.name = Keret nélküli ablak setting.borderlesswindow.name.windows = Keret nélküli teljes képernyő -setting.borderlesswindow.description = A változások alkalmazásához újraindításra lehet szükség. -setting.fps.name = FPS, memória használat és ping megjelenítése +setting.borderlesswindow.description = A változtatások érvénybe lépéséhez újraindításra lehet szükség. +setting.fps.name = FPS, memóriahasználat és ping megjelenítése setting.console.name = Konzol engedélyezése setting.smoothcamera.name = Egyenletes kamera setting.vsync.name = VSync -setting.pixelate.name = Pixeles +setting.pixelate.name = Pixelesítés setting.minimap.name = Minitérkép megjelenítése -setting.coreitems.name = A Támaszpontban lévő nyersanyagok megjelenítése -setting.position.name = A játékos pozíciójának megjelenítése +setting.coreitems.name = Támaszpontban lévő nyersanyagok megjelenítése +setting.position.name = Játékos pozíciójának megjelenítése setting.mouseposition.name = Egér pozíciójának megjelenítése -setting.musicvol.name = Zene hangerő -setting.atmosphere.name = Bolygó atmoszféra megjelenítése +setting.musicvol.name = Zene hangereje +setting.atmosphere.name = Bolygóatmoszféra megjelenítése setting.drawlight.name = Sötét/világos fényhatások setting.ambientvol.name = Környezeti hangerő setting.mutemusic.name = Zene némítása -setting.sfxvol.name = SFX hangerő +setting.sfxvol.name = Hanghatások hangereje setting.mutesound.name = Hang némítása setting.crashreport.name = Névtelen összeomlási jelentések setting.savecreate.name = Automatikus mentés setting.steampublichost.name = Nyilvános játék láthatósága -setting.playerlimit.name = Játékos limit +setting.playerlimit.name = Játékoskorlát setting.chatopacity.name = Csevegő átlátszatlansága setting.lasersopacity.name = Villanyvezeték átlátszatlansága setting.bridgeopacity.name = Híd átlátszatlansága -setting.playerchat.name = Játékos szóbuborékok megjelenítése -setting.showweather.name = Időjárás grafika megjelenítése +setting.playerchat.name = Játékosok csevegőbuborékainak megjelenítése +setting.showweather.name = Időjárásgrafika megjelenítése setting.hidedisplays.name = Logikai kijelzők elrejtése -setting.macnotch.name = A felület igazítása a bevágás megjelenítéséhez -setting.macnotch.description = A változtatások alkalmazásához újra kell indítani +setting.macnotch.name = A felület igazítása a kijelző bevágásához +setting.macnotch.description = A változtatások érvénybe lépéséhez újraindítás szükséges steam.friendsonly = Csak barátok -steam.friendsonly.tooltip = Csak a Steam-barátok tudnak kapcsolódni a játékodhoz.\nHa nem jelölöd be ezt a négyzetet, a játékod nyilvános lesz - bárki kapcsolódhat hozzá. +steam.friendsonly.tooltip = Csak a Steam-barátok tudnak kapcsolódni a játékodhoz.\nHa nem jelölöd be ezt a négyzetet, a játékod nyilvános lesz – bárki kapcsolódhat hozzá. public.beta = Ne feledd, hogy a játék béta verziójában nem tudsz nyilvános szobát nyitni. -uiscale.reset = Az UI mérete megváltozott.\nAz "OK" gombbal megerősítheted ezt a méretet.\n[scarlet]Visszavonás és kilépés [accent] {0}[] másodperc múlva... -uiscale.cancel = Mégsem és kilépés +uiscale.reset = A felület mérete megváltozott.\nAz „OK” gombbal megerősítheted ezt a méretet.\n[scarlet]Visszavonás és kilépés [accent] {0}[] másodperc múlva... +uiscale.cancel = Mégse és kilépés setting.bloom.name = Bloom -keybind.title = Gyorsbillentyűk +keybind.title = Billentyűk átállítása keybinds.mobile = [scarlet]A legtöbb billentyűfunkció mobilon nem működik. Csak a mozgás támogatott. category.general.name = Általános category.view.name = Nézet -category.command.name = Egység parancs +category.command.name = Egységparancs category.multiplayer.name = Többjátékos -category.blocks.name = Blokk választás +category.blocks.name = Blokkválasztás placement.blockselectkeys = \n[lightgray]Kulcs: [{0}, keybind.respawn.name = Újraéledés keybind.control.name = Egység irányítása keybind.clear_building.name = Épület törlése keybind.press = Nyomj meg egy gombot... -keybind.press.axis = Nyomj meg egy kart, vagy gombot... -keybind.screenshot.name = Pálya képernyőkép +keybind.press.axis = Nyomj meg egy kart vagy gombot... +keybind.screenshot.name = Pálya képernyőképe keybind.toggle_power_lines.name = Villanyvezetékek be/ki -keybind.toggle_block_status.name = Épület állapot be/ki +keybind.toggle_block_status.name = Blokkállapotok be/ki keybind.move_x.name = Mozgás vízszintesen keybind.move_y.name = Mozgás függőlegesen keybind.mouse_move.name = Egér követése keybind.pan.name = Felderítés keybind.boost.name = Erősítés keybind.command_mode.name = Parancs mód -keybind.command_queue.name = Egység parancsok sorba állítása -keybind.create_control_group.name = Vezérlő csoport készítése +keybind.command_queue.name = Egységparancsok sorba állítása +keybind.create_control_group.name = Vezérlőcsoport készítése keybind.cancel_orders.name = Parancsok visszavonása keybind.unit_stance_shoot.name = Egység viselkedése: lövés @@ -1206,15 +1206,15 @@ keybind.unit_stance_pursue_target.name = Egység viselkedése: célpont követé keybind.unit_stance_patrol.name = Egység viselkedése: járőrözés keybind.unit_stance_ram.name = Egység viselkedése: ütközés -keybind.unit_command_move = Egység parancs: mozgás -keybind.unit_command_repair = Egység parancs: javítás -keybind.unit_command_rebuild = Egység parancs: újraépítés -keybind.unit_command_assist = Egység parancs: támogatás -keybind.unit_command_mine = Egység parancs: bányászás -keybind.unit_command_boost = Egység parancs: erősítés -keybind.unit_command_load_units = Egység parancs: egységek berakodása a rakományszállítóba -keybind.unit_command_load_blocks = Egység parancs: blokkok berakodása a rakományszállítóba -keybind.unit_command_unload_payload = Egység parancs: kirakodás a rakományszállítóból +keybind.unit_command_move = Egységparancs: mozgás +keybind.unit_command_repair = Egységparancs: javítás +keybind.unit_command_rebuild = Egységparancs: újjáépítés +keybind.unit_command_assist = Egységparancs: támogatás +keybind.unit_command_mine = Egységparancs: bányászás +keybind.unit_command_boost = Egységparancs: erősítés +keybind.unit_command_load_units = Egységparancs: egységek berakodása +keybind.unit_command_load_blocks = Egységparancs: blokkok berakodása +keybind.unit_command_unload_payload = Egységparancs: kirakodás keybind.rebuild_select.name = Régió újjáépítése keybind.schematic_select.name = Terület kijelölése @@ -1227,24 +1227,24 @@ keybind.block_select_left.name = Blokk váltás balra keybind.block_select_right.name = Blokk váltás jobbra keybind.block_select_up.name = Blokk váltás fel keybind.block_select_down.name = Blokk váltás le -keybind.block_select_01.name = Kategória/blokk választás 1 -keybind.block_select_02.name = Kategória/blokk választás 2 -keybind.block_select_03.name = Kategória/blokk választás 3 -keybind.block_select_04.name = Kategória/blokk választás 4 -keybind.block_select_05.name = Kategória/blokk választás 5 -keybind.block_select_06.name = Kategória/blokk választás 6 -keybind.block_select_07.name = Kategória/blokk választás 7 -keybind.block_select_08.name = Kategória/blokk választás 8 -keybind.block_select_09.name = Kategória/blokk választás 9 -keybind.block_select_10.name = Kategória/blokk választás 10 +keybind.block_select_01.name = 1. kategória/blokk választása +keybind.block_select_02.name = 2. kategória/blokk választása +keybind.block_select_03.name = 3. kategória/blokk választása +keybind.block_select_04.name = 4. kategória/blokk választása +keybind.block_select_05.name = 5. kategória/blokk választása +keybind.block_select_06.name = 6. kategória/blokk választása +keybind.block_select_07.name = 7. kategória/blokk választása +keybind.block_select_08.name = 8. kategória/blokk választása +keybind.block_select_09.name = 9. kategória/blokk választása +keybind.block_select_10.name = 10. kategória/blokk választása keybind.fullscreen.name = Teljes képernyő be/ki -keybind.select.name = Kiválasztás/Lövés +keybind.select.name = Kiválasztás/lövés keybind.diagonal_placement.name = Átlós elhelyezés -keybind.pick.name = Blokk másolása +keybind.pick.name = Blokk kiválasztása keybind.break_block.name = Blokk törlése -keybind.select_all_units.name = Minden egység kijelölése -keybind.select_all_unit_factories.name = Minden egység gyár kijelölése -keybind.deselect.name = Blokk választás törlése +keybind.select_all_units.name = Összes egység kijelölése +keybind.select_all_unit_factories.name = Összes egységgyár kijelölése +keybind.deselect.name = Blokkkijelölés törlése keybind.pickupCargo.name = Rakomány felvétele keybind.dropCargo.name = Rakomány lerakása keybind.shoot.name = Lövés @@ -1253,79 +1253,79 @@ keybind.menu.name = Menü keybind.pause.name = Szünet keybind.pause_building.name = Építés szüneteltetése/folytatása keybind.minimap.name = Minitérkép -keybind.planet_map.name = Bolygó térkép +keybind.planet_map.name = Bolygótérkép keybind.research.name = Fejlesztés keybind.block_info.name = Blokk infó keybind.chat.name = Csevegés keybind.player_list.name = Játékosok listája keybind.console.name = Konzol keybind.rotate.name = Forgatás -keybind.rotateplaced.name = Meglévő forgatása (tartsd nyomva) -keybind.toggle_menus.name = Menük váltása +keybind.rotateplaced.name = Meglévő forgatása (nyomva tartva) +keybind.toggle_menus.name = Menük be/ki keybind.chat_history_prev.name = Csevegés görgetése felfelé keybind.chat_history_next.name = Csevegés görgetése lefelé keybind.chat_scroll.name = Csevegés görgetése -keybind.chat_mode.name = Csevegés mód megváltoztatása +keybind.chat_mode.name = Csevegési mód megváltoztatása keybind.drop_unit.name = Egység eldobása -keybind.zoom_minimap.name = Zoom a minitérképen +keybind.zoom_minimap.name = Nagyítás a minitérképen mode.help.title = Játékmódok leírása -mode.survival.name = Túlélő -mode.survival.description = A normál mód. Korlátozott nyersanyag és automatikusan érkező hullámok.\n[gray]Szükséges hozzá egy ellenséges kezdőpont a pályán. +mode.survival.name = Túlélés +mode.survival.description = A normál mód. Korlátozott nyersanyagok, és automatikusan érkező hullámok.\n[gray]Ellenséges kezdőpont szükséges hozzá a pályán. mode.sandbox.name = Homokozó mode.sandbox.description = Végtelen nyersanyagforrás, nincs időzítés a hullámokhoz. mode.editor.name = Szerkesztő mode.pvp.name = PvP -mode.pvp.description = Harcolj más játékosok ellen.\n[gray]Szükséges hozzá legalább két különböző színű Támaszpont a pályán. +mode.pvp.description = Harcolj más játékosok ellen.\n[gray]Legalább két különböző színű támaszpont szükséges hozzá a pályán. mode.attack.name = Támadás -mode.attack.description = Pusztítsd el az ellenség bázisát. \n[gray]Szükséges hozzá egy piros Támaszpont a pályán. +mode.attack.description = Pusztítsd el az ellenség bázisát. \n[gray]Piros támaszpont szükséges hozzá a pályán. mode.custom = Egyéni szabályok rules.invaliddata = Érvénytelen adatok vannak a vágólapon. -rules.hidebannedblocks = Tiltott épületek elrejtése +rules.hidebannedblocks = Tiltott blokkok elrejtése rules.infiniteresources = Végtelen nyersanyagforrás -rules.onlydepositcore = Csak Támaszpontok elhelyezése engedélyezett +rules.onlydepositcore = Csak a támaszpontok elhelyezése engedélyezett rules.derelictrepair = Az elhagyatott épületek javításának engedélyezése -rules.reactorexplosions = Reaktor robbanás -rules.coreincinerates = Többlet nyersanyagok megsemmisítése a Támaszpontban -rules.disableworldprocessors = Világprocesszorok letiltása +rules.reactorexplosions = Reaktorrobbanások +rules.coreincinerates = Többletnyersanyagok megsemmisítése a támaszpontban +rules.disableworldprocessors = Világfeldolgozók letiltása rules.schematic = Vázlatok engedélyezése -rules.wavetimer = Hullám időzítő -rules.wavesending = Hullám küldése +rules.wavetimer = Hullámok időzítése +rules.wavesending = Hullámok küldése rules.waves = Hullámok -rules.attack = Támadás mód -rules.buildai = Épületalkotó AI -rules.buildaitier = Épületalkotó AI szintje -rules.rtsai = RTS AI [red](WIP) -rules.rtsminsquadsize = Minimális osztag méret -rules.rtsmaxsquadsize = Maximális osztag méret -rules.rtsminattackweight = Minimális támadási "lépéselőny" -rules.cleanupdeadteams = A legyőzött csapatépületek törlése (PvP) +rules.attack = Támadási mód +rules.buildai = Bázisépítő MI +rules.buildaitier = Építő MI szintje +rules.rtsai = RTS MI [red](WIP) +rules.rtsminsquadsize = Minimális osztagméret +rules.rtsmaxsquadsize = Maximális osztagméret +rules.rtsminattackweight = Minimális támadási súly +rules.cleanupdeadteams = Legyőzött csapatok épületeinek törlése (PvP) rules.corecapture = Támaszpont elfoglalása megsemmisítéskor -rules.polygoncoreprotection = Poligonális Támaszpont védelem +rules.polygoncoreprotection = Poligonális támaszpontvédelem rules.placerangecheck = Elhelyezési tartomány ellenőrzése -rules.enemyCheat = Végtelen ellenséges csapat erőforrások -rules.blockhealthmultiplier = Épület életpont szorzó -rules.blockdamagemultiplier = Épület sebzés szorzó -rules.unitbuildspeedmultiplier = Egység gyártási sebesség szorzó -rules.unitcostmultiplier = Egységköltség szorzó -rules.unithealthmultiplier = Egység életpont szorzó -rules.unitdamagemultiplier = Egység sebzés szorzó -rules.unitcrashdamagemultiplier = Egység ütközési sebzés szorzó -rules.solarmultiplier = Napenergia szorzó -rules.unitcapvariable = A Támaszpontok befolyásolják a legyártható egységek darabszámát -rules.unitcap = Alap egység-darabszám -rules.limitarea = Játékterület korlátozása a pályán -rules.enemycorebuildradius = Ellenséges Támaszpont körüli tiltott zóna sugara:[lightgray] (csempe) +rules.enemyCheat = Végtelen ellenséges csapaterőforrások +rules.blockhealthmultiplier = Épület életpontszorzója +rules.blockdamagemultiplier = Épület sebzésszorzója +rules.unitbuildspeedmultiplier = Egységgyártás sebességének szorzója +rules.unitcostmultiplier = Egység költségszorzója +rules.unithealthmultiplier = Egység életpontszorzója +rules.unitdamagemultiplier = Egység sebzésszorzója +rules.unitcrashdamagemultiplier = Egység ütközési sebzésszorzója +rules.solarmultiplier = Napenergia szorzója +rules.unitcapvariable = A támaszpontok befolyásolják a gyártható egységek darabszámát +rules.unitcap = Alap egységdarabszám +rules.limitarea = Játékterület korlátozása +rules.enemycorebuildradius = Ellenséges támaszpont körüli tiltott zóna sugara:[lightgray] (csempe) rules.wavespacing = A hullámok közötti szünetek ideje:[lightgray] (mp) rules.initialwavespacing = Az első hullám előtti szünet ideje:[lightgray] (mp) -rules.buildcostmultiplier = Építési költség szorzó -rules.buildspeedmultiplier = Építési sebesség szorzó -rules.deconstructrefundmultiplier = Bontási visszatérítés szorzó +rules.buildcostmultiplier = Építési költség szorzója +rules.buildspeedmultiplier = Építési sebesség szorzója +rules.deconstructrefundmultiplier = Bontási visszatérítés szorzója rules.waitForWaveToEnd = Az ellenség kivárja a korábbi hullám végét -rules.wavelimit = Az utolsó hullám után legyen vége a játéknak +rules.wavelimit = A pálya az utolsó hullám után ér véget rules.dropzoneradius = A ledobási zóna sugara:[lightgray] (csempe) rules.unitammo = Az egységeknek lőszer kell [red](törölhető) -rules.enemyteam = Elenséges csapat +rules.enemyteam = Ellenséges csapat rules.playerteam = Saját csapat rules.title.waves = Hullámok rules.title.resourcesbuilding = Nyersanyagforrások és épületek @@ -1339,7 +1339,7 @@ rules.lighting = Világítás rules.fog = Köd rules.fire = Tűz rules.anyenv = -rules.explosions = Épület/egység robbanás sebzés +rules.explosions = Épület/egység robbanási sebzése rules.ambientlight = Háttérvilágítás rules.weather = Időjárás rules.weather.frequency = Gyakoriság: @@ -1351,7 +1351,7 @@ content.liquid.name = Folyadékok content.unit.name = Egységek content.block.name = Blokkok content.status.name = Állapothatások -content.sector.name = Szektor +content.sector.name = Szektorok content.team.name = Csapatok wallore = (Fal) @@ -1366,7 +1366,7 @@ item.silicon.name = Szilícium item.plastanium.name = Műanyag item.phase-fabric.name = Tóritkvarc item.surge-alloy.name = Elektrometál -item.spore-pod.name = Spóra Kapszula +item.spore-pod.name = Spórakapszula item.sand.name = Homok item.blast-compound.name = Robbanóelegy item.pyratite.name = Piratit @@ -1377,19 +1377,19 @@ item.beryllium.name = Berillium item.tungsten.name = Volfrám item.oxide.name = Oxid item.carbide.name = Karbid -item.dormant-cyst.name = Nyugvó-Ciszta +item.dormant-cyst.name = Nyugvó ciszta liquid.water.name = Víz liquid.slag.name = Salak liquid.oil.name = Olaj liquid.cryofluid.name = Hűtőfolyadék liquid.neoplasm.name = Neoplazma -liquid.arkycite.name = Arkycit +liquid.arkycite.name = Arkicit liquid.gallium.name = Gallium liquid.ozone.name = Ózon liquid.hydrogen.name = Hidrogén liquid.nitrogen.name = Nitrogén -liquid.cyanogen.name = Cianogén +liquid.cyanogen.name = Dicián unit.dagger.name = Dagger unit.mace.name = Mace @@ -1449,159 +1449,159 @@ unit.evoke.name = Evoke unit.incite.name = Incite unit.emanate.name = Emanate unit.manifold.name = Manifold -unit.assembly-drone.name = Assembly Drone +unit.assembly-drone.name = Összeszerelő drón unit.latum.name = Latum unit.renale.name = Renale block.parallax.name = Parallax block.cliff.name = Szirt -block.sand-boulder.name = Homok Szikla -block.basalt-boulder.name = Bazalt Szikla +block.sand-boulder.name = Homokszikla +block.basalt-boulder.name = Bazaltszikla block.grass.name = Fű block.molten-slag.name = Salak -block.pooled-cryofluid.name = Hűtőfolyadék Medence +block.pooled-cryofluid.name = Hűtőfolyadék block.space.name = Űr block.salt.name = Só -block.salt-wall.name = Só Fal +block.salt-wall.name = Sófal block.pebbles.name = Kavicsok block.tendrils.name = Indák -block.sand-wall.name = Homok Fal -block.spore-pine.name = Spóra Fenyő -block.spore-wall.name = Spóra Fal +block.sand-wall.name = Homokfal +block.spore-pine.name = Spórafenyő +block.spore-wall.name = Spórafal block.boulder.name = Szikla -block.snow-boulder.name = Hó Szikla -block.snow-pine.name = Havas Fenyő +block.snow-boulder.name = Havas szikla +block.snow-pine.name = Havas fenyő block.shale.name = Pala -block.shale-boulder.name = Pala Szikla +block.shale-boulder.name = Palaszikla block.moss.name = Moha block.shrubs.name = Cserjék -block.spore-moss.name = Spóra Moha -block.shale-wall.name = Pala Fal -block.scrap-wall.name = Törmelék Fal -block.scrap-wall-large.name = Nagy Törmelék Fal -block.scrap-wall-huge.name = Hatalmas Törmelék Fal -block.scrap-wall-gigantic.name = Gigantikus Törmelék Fal +block.spore-moss.name = Spóramoha +block.shale-wall.name = Palafal +block.scrap-wall.name = Törmelékfal +block.scrap-wall-large.name = Nagy törmelékfal +block.scrap-wall-huge.name = Hatalmas törmelékfal +block.scrap-wall-gigantic.name = Gigantikus törmelékfal block.thruster.name = Hajtómű block.kiln.name = Kemence -block.graphite-press.name = Grafit Préselő -block.multi-press.name = Grafit Sajtoló -block.constructing = {0} [lightgray](Építés) +block.graphite-press.name = Grafitprés +block.multi-press.name = Grafitsajtoló +block.constructing = {0} [lightgray](építés alatt) block.spawn.name = Ellenséges kezdőpont -block.core-shard.name = Szilánk -block.core-foundation.name = Alapítvány -block.core-nucleus.name = Magnum -block.deep-water.name = Mély Víz +block.core-shard.name = Támaszpont: Szilánk +block.core-foundation.name = Támaszpont: Alapítvány +block.core-nucleus.name = Támaszpont: Atommag +block.deep-water.name = Mély víz block.shallow-water.name = Víz -block.tainted-water.name = Szennyezett Víz -block.deep-tainted-water.name = Mély Szennyezett Víz -block.darksand-tainted-water.name = Sötét Homokkal Szennyezett Víz +block.tainted-water.name = Szennyezett víz +block.deep-tainted-water.name = Mély szennyezett víz +block.darksand-tainted-water.name = Sötét homokkal szennyezett víz block.tar.name = Kátrány block.stone.name = Kő block.sand-floor.name = Homok -block.darksand.name = Sötét Homok +block.darksand.name = Sötét homok block.ice.name = Jég block.snow.name = Hó block.crater-stone.name = Kráterek block.sand-water.name = Homokvíz -block.darksand-water.name = Sötét Homokvíz +block.darksand-water.name = Sötét homokvíz block.char.name = Faszén block.dacite.name = Dácit block.rhyolite.name = Riolit -block.dacite-wall.name = Dácit Fal -block.dacite-boulder.name = Dácit Szikla +block.dacite-wall.name = Dácitfal +block.dacite-boulder.name = Dácitszikla block.ice-snow.name = Jéghó -block.stone-wall.name = Kő Fal -block.ice-wall.name = Jég Fal -block.snow-wall.name = Hó Fal -block.dune-wall.name = Dűne Fal +block.stone-wall.name = Kőfal +block.ice-wall.name = Jégfal +block.snow-wall.name = Hófal +block.dune-wall.name = Dűnefal block.pine.name = Fenyő block.dirt.name = Sár -block.dirt-wall.name = Sár Fal +block.dirt-wall.name = Sárfal block.mud.name = Iszap -block.white-tree-dead.name = Kiszáradt Fehér Fa -block.white-tree.name = Fehér Fa -block.spore-cluster.name = Spóra Fürt -block.metal-floor.name = Fém Padló 1 -block.metal-floor-2.name = Fém Padló 2 -block.metal-floor-3.name = Fém Padló 3 -block.metal-floor-4.name = Fém Padló 4 -block.metal-floor-5.name = Fém Padló 5 -block.metal-floor-damaged.name = Sérült Fém Padló -block.dark-panel-1.name = Sötét Panel 1 -block.dark-panel-2.name = Sötét Panel 2 -block.dark-panel-3.name = Sötét Panel 3 -block.dark-panel-4.name = Sötét Panel 4 -block.dark-panel-5.name = Sötét Panel 5 -block.dark-panel-6.name = Sötét Panel 6 -block.dark-metal.name = Sötét Fém +block.white-tree-dead.name = Kiszáradt fehér fa +block.white-tree.name = Fehér fa +block.spore-cluster.name = Spórafürt +block.metal-floor.name = 1. fémpadló +block.metal-floor-2.name = 2. fémpadló +block.metal-floor-3.name = 3. fémpadló +block.metal-floor-4.name = 4. fémpadló +block.metal-floor-5.name = 5. fémpadló +block.metal-floor-damaged.name = Sérült fémpadló +block.dark-panel-1.name = 1. sötét panel +block.dark-panel-2.name = 2. sötét panel +block.dark-panel-3.name = 3. sötét panel +block.dark-panel-4.name = 4. sötét panel +block.dark-panel-5.name = 5. sötét panel +block.dark-panel-6.name = 6. sötét panel +block.dark-metal.name = Sötét fém block.basalt.name = Bazalt -block.hotrock.name = Forró Kőzet -block.magmarock.name = Magmás Kőzet -block.copper-wall.name = Réz Fal -block.copper-wall-large.name = Nagy Réz Fal -block.titanium-wall.name = Titán Fal -block.titanium-wall-large.name = Nagy Titán Fal -block.plastanium-wall.name = Műanyag Fal -block.plastanium-wall-large.name = Nagy Műanyag Fal -block.phase-wall.name = Tóritkvarc Fal -block.phase-wall-large.name = Nagy Tóritkvarc Fal -block.thorium-wall.name = Tórium Fal -block.thorium-wall-large.name = Nagy Tórium Fal +block.hotrock.name = Forró kőzet +block.magmarock.name = Magmás kőzet +block.copper-wall.name = Rézfal +block.copper-wall-large.name = Nagy rézfal +block.titanium-wall.name = Titánfal +block.titanium-wall-large.name = Nagy titánfal +block.plastanium-wall.name = Műanyagfal +block.plastanium-wall-large.name = Nagy műanyagfal +block.phase-wall.name = Tóritkvarcfal +block.phase-wall-large.name = Nagy tóritkvarcfal +block.thorium-wall.name = Tóriumfal +block.thorium-wall-large.name = Nagy tóriumfal block.door.name = Ajtó -block.door-large.name = Nagy Ajtó +block.door-large.name = Nagy ajtó block.duo.name = Duo block.scorch.name = Scorch block.scatter.name = Scatter block.hail.name = Hail block.lancer.name = Lancer block.conveyor.name = Szállítószalag -block.titanium-conveyor.name = Titán Szállítószalag -block.plastanium-conveyor.name = Műanyag Szállítószalag -block.armored-conveyor.name = Páncélozott Szállítószalag -block.junction.name = Elágazás +block.titanium-conveyor.name = Titán szállítószalag +block.plastanium-conveyor.name = Műanyag szállítószalag +block.armored-conveyor.name = Páncélozott szállítószalag +block.junction.name = Átkötés block.router.name = Elosztó -block.distributor.name = Terjesztő +block.distributor.name = Szétosztó block.sorter.name = Válogató -block.inverted-sorter.name = Inverz Válogató +block.inverted-sorter.name = Fordított válogató block.message.name = Üzenet -block.reinforced-message.name = Megerősített Üzenet -block.world-message.name = Világ Üzenet -block.world-switch.name = Világ Kapcsoló +block.reinforced-message.name = Megerősített üzenet +block.world-message.name = Világüzenet +block.world-switch.name = Világkapcsoló block.illuminator.name = Világítótest -block.overflow-gate.name = Alul-folyó Kapu -block.underflow-gate.name = Túlfolyó Kapu -block.silicon-smelter.name = Szilícium Kohó +block.overflow-gate.name = Túlcsorduló kapu +block.underflow-gate.name = Alulcsorduló kapu +block.silicon-smelter.name = Szilícium kohó block.phase-weaver.name = Tóritkvarcképző block.pulverizer.name = Porlasztó -block.cryofluid-mixer.name = Hűtőfolyadék Keverő +block.cryofluid-mixer.name = Hűtőfolyadék-keverő block.melter.name = Olvasztó block.incinerator.name = Törmelékégető -block.spore-press.name = Spóra Sajtoló +block.spore-press.name = Spóraprés block.separator.name = Leválasztó -block.coal-centrifuge.name = Szén Centrifuga +block.coal-centrifuge.name = Széncentrifuga block.power-node.name = Villanyoszlop -block.power-node-large.name = Nagy Villanyoszlop +block.power-node-large.name = Nagy villanyoszlop block.surge-tower.name = Villanytorony -block.diode.name = Akkumulátor Dióda +block.diode.name = Akkumulátordióda block.battery.name = Akkumulátor -block.battery-large.name = Nagy Akkumulátor -block.combustion-generator.name = Belső-égetésű Erőmű +block.battery-large.name = Nagy akkumulátor +block.combustion-generator.name = Égetőerőmű block.steam-generator.name = Gőzerőmű -block.differential-generator.name = Differenciál Erőmű -block.impact-reactor.name = Ütközéses Erőmű -block.mechanical-drill.name = Mechanikus Fúró -block.pneumatic-drill.name = Pneumatikus Fúró -block.laser-drill.name = Lézer Vágó -block.water-extractor.name = Vízkiválasztó +block.differential-generator.name = Differenciál-erőmű +block.impact-reactor.name = Ütközéses erőmű +block.mechanical-drill.name = Mechanikus fúró +block.pneumatic-drill.name = Pneumatikus fúró +block.laser-drill.name = Lézerfúró +block.water-extractor.name = Vízkinyerő block.cultivator.name = Betakarító block.conduit.name = Csővezeték -block.mechanical-pump.name = Mechanikus Szivattyú -block.item-source.name = Nyersanyag Forrás -block.item-void.name = Nyersanyag Hiány -block.liquid-source.name = Folyadék Forrás -block.liquid-void.name = Folyadék Hiány -block.power-void.name = Áram Hiány -block.power-source.name = Áram Forrás +block.mechanical-pump.name = Mechanikus szivattyú +block.item-source.name = Nyersanyagforrás +block.item-void.name = Nyersanyagnyelő +block.liquid-source.name = Folyadékforrás +block.liquid-void.name = Folyadéknyelő +block.power-void.name = Áramnyelő +block.power-source.name = Áramforrás block.unloader.name = Kirakodó block.vault.name = Raktár block.wave.name = Wave @@ -1609,199 +1609,199 @@ block.tsunami.name = Tsunami block.swarmer.name = Swarmer block.salvo.name = Salvo block.ripple.name = Ripple -block.phase-conveyor.name = Tóritkvarc Szállítószalag Híd -block.bridge-conveyor.name = Szállítószalag Híd -block.plastanium-compressor.name = Műanyag Sűrítő -block.pyratite-mixer.name = Piratit Keverő -block.blast-mixer.name = Robbanóelegy Keverő +block.phase-conveyor.name = Tóritkvarc szállítószalag +block.bridge-conveyor.name = Szállítószalag-híd +block.plastanium-compressor.name = Műanyagsűrítő +block.pyratite-mixer.name = Piratitkeverő +block.blast-mixer.name = Robbanóelegy-keverő block.solar-panel.name = Napelem -block.solar-panel-large.name = Nagy Napelem +block.solar-panel-large.name = Nagy napelem block.oil-extractor.name = Olajleválasztó -block.repair-point.name = Javítási Pont -block.repair-turret.name = Javító Löveg -block.pulse-conduit.name = Impulzus Csővezeték -block.plated-conduit.name = Lemezelt Csővezeték -block.phase-conduit.name = Tóritkvarc Csővezeték Híd -block.liquid-router.name = Folyadék Elosztó -block.liquid-tank.name = Folyadék Tartály -block.liquid-container.name = Folyadék Tároló -block.liquid-junction.name = Csővezeték Átkötés -block.bridge-conduit.name = Csővezeték Híd -block.rotary-pump.name = Fogaskerék Szivattyú -block.thorium-reactor.name = Tórium Erőmű +block.repair-point.name = Javítási pont +block.repair-turret.name = Javítótorony +block.pulse-conduit.name = Impulzus-csővezeték +block.plated-conduit.name = Lemezelt csővezeték +block.phase-conduit.name = Tóritkvarc-csővezeték +block.liquid-router.name = Folyadékelosztó +block.liquid-tank.name = Folyadéktartály +block.liquid-container.name = Folyadéktároló +block.liquid-junction.name = Csővezeték-átkötés +block.bridge-conduit.name = Csővezetékhíd +block.rotary-pump.name = Fogaskerekes szivattyú +block.thorium-reactor.name = Tóriumerőmű block.mass-driver.name = Tömegmozgató -block.blast-drill.name = Légrobbanásos Fúró -block.impulse-pump.name = Impulzus Szivattyú +block.blast-drill.name = Légrobbanásos fúró +block.impulse-pump.name = Impulzusszivattyú block.thermal-generator.name = Hőerőmű -block.surge-smelter.name = Elektrometál Olvasztó +block.surge-smelter.name = Elektrometál-olvasztó block.mender.name = Foltozó -block.mend-projector.name = Foltozó Projektor -block.surge-wall.name = Elektrometál Fal -block.surge-wall-large.name = Nagy Elektrometál Fal +block.mend-projector.name = Foltozó projektor +block.surge-wall.name = Elektrometálfal +block.surge-wall-large.name = Nagy elektrometálfal block.cyclone.name = Cyclone block.fuse.name = Fuse -block.shock-mine.name = Sokkoló Taposóakna -block.overdrive-projector.name = Túlhajtó Projektor -block.force-projector.name = Erő Projektor +block.shock-mine.name = Sokkoló taposóakna +block.overdrive-projector.name = Túlhajtó kivetítő +block.force-projector.name = Erőkivetítő block.arc.name = Arc -block.rtg-generator.name = RTG Erőmű +block.rtg-generator.name = RTG erőmű block.spectre.name = Spectre block.meltdown.name = Meltdown block.foreshadow.name = Foreshadow block.container.name = Konténer -block.launch-pad.name = Kilövő Állás +block.launch-pad.name = Kilövőállás block.segment.name = Segment -block.ground-factory.name = Földi Egység Gyár -block.air-factory.name = Repülőgép Gyár -block.naval-factory.name = Hadihajó Gyár -block.additive-reconstructor.name = Additív Újratervező -block.multiplicative-reconstructor.name = Multiplikatív Újratervező -block.exponential-reconstructor.name = Exponenciális Újratervező -block.tetrative-reconstructor.name = Tetratív Újratervező -block.payload-conveyor.name = Rakomány Szállítószalag -block.payload-router.name = Rakomány Elosztó +block.ground-factory.name = Földiegységgyár +block.air-factory.name = Repülőgépgyár +block.naval-factory.name = Hadihajógyár +block.additive-reconstructor.name = Additív újratervező +block.multiplicative-reconstructor.name = Multiplikatív újratervező +block.exponential-reconstructor.name = Exponenciális újratervező +block.tetrative-reconstructor.name = Tetratív újratervező +block.payload-conveyor.name = Rakományszállító-szalag +block.payload-router.name = Rakomány-elosztó block.duct.name = Szállítószalag -block.duct-router.name = Szállítószalag Elosztó -block.duct-bridge.name = Szállítószalag Híd -block.large-payload-mass-driver.name = Nagy Rakomány Tömegmozgató -block.payload-void.name = Rakomány Megsemmisítő -block.payload-source.name = Rakomány Készítő -block.disassembler.name = Szétválasztó -block.silicon-crucible.name = Szilícium Olvasztó -block.overdrive-dome.name = Túlhajtó Búra -block.interplanetary-accelerator.name = Bolygóközi Gyorsító +block.duct-router.name = Szállítószalag-elosztó +block.duct-bridge.name = Szállítószalaghíd +block.large-payload-mass-driver.name = Nagy rakomány-tömegmozgató +block.payload-void.name = Rakománynyelő +block.payload-source.name = Rakományforrás +block.disassembler.name = Szétszerelő +block.silicon-crucible.name = Szilíciumolvasztó +block.overdrive-dome.name = Túlhajtó búra +block.interplanetary-accelerator.name = Bolygóközi gyorsító block.constructor.name = Építő -block.constructor.description = Akár 2x2 csempe méretű szerkezeteket is gyárt. -block.large-constructor.name = Nagy Építő -block.large-constructor.description = Akár 4x4 csempe méretű szerkezeteket is gyárt. +block.constructor.description = Legfeljebb 2×2-es csempeméretű épületeket gyárt. +block.large-constructor.name = Nagy építő +block.large-constructor.description = Akár 4×4-es csempeméretű épületeket is gyárt. block.deconstructor.name = Lebontó -block.deconstructor.description = Lebontja a szerkezeteket és az egységeket. Visszaadja az építési költség 100%-át. -block.payload-loader.name = Rakomány Csomagoló -block.payload-loader.description = A folyadékokat és a nyersanyagokat csomagolja nagyobb blokkokba. -block.payload-unloader.name = Rakomány Kicsomagoló -block.payload-unloader.description = A folyadékokból és nyersanyagokból álló blokkokat csomagolja ki. -block.heat-source.name = Hő Forrás -block.heat-source.description = Egy 1x1-es blokk, amely gyakorlatilag végtelen Hőt ad. +block.deconstructor.description = Lebontja az épületeket és az egységeket. Visszaadja az építési költség 100%-át. +block.payload-loader.name = Rakománycsomagoló +block.payload-loader.description = A folyadékokat és a nyersanyagokat blokkokba csomagolja. +block.payload-unloader.name = Rakománykibontó +block.payload-unloader.description = Kibontja a folyadékokból és nyersanyagokból álló blokkokat. +block.heat-source.name = Hőforrás +block.heat-source.description = Nagy hőmennyiséget bocsát ki. Csak homokozó módban. #Erekir block.empty.name = Üres -block.rhyolite-crater.name = Riolit Kráter -block.rough-rhyolite.name = Durva Riolit +block.rhyolite-crater.name = Riolit kráter +block.rough-rhyolite.name = Durva riolit block.regolith.name = Regolit -block.yellow-stone.name = Sárga Kő -block.carbon-stone.name = Szén Kő -block.ferric-stone.name = Vasas Kő -block.ferric-craters.name = Vasas Kráterek -block.beryllic-stone.name = Berilliumos Kő -block.crystalline-stone.name = Kristályos Kő -block.crystal-floor.name = Kristály Talaj -block.yellow-stone-plates.name = Sárga Kőlemezek -block.red-stone.name = Vörös Kő -block.dense-red-stone.name = Sűrű Vörös Kő -block.red-ice.name = Vörös Jég -block.arkycite-floor.name = Arkycit -block.arkyic-stone.name = Arkycites Kő -block.rhyolite-vent.name = Riolit Kürtő -block.carbon-vent.name = Szén Kürtő -block.arkyic-vent.name = Arkycites Kürtő -block.yellow-stone-vent.name = Sárga Kő Kürtő -block.red-stone-vent.name = Vörös Kő Kürtő -block.crystalline-vent.name = Kristályos Kürtő -block.redmat.name = Vörös Padló -block.bluemat.name = Kék Padló -block.core-zone.name = Támaszpont Zóna -block.regolith-wall.name = Regolit Fal -block.yellow-stone-wall.name = Sárga Kő Fal -block.rhyolite-wall.name = Riolit Fal -block.carbon-wall.name = Szén Fal -block.ferric-stone-wall.name = Vasas-Kő Fal -block.beryllic-stone-wall.name = Berilliumos-Kő Fal -block.arkyic-wall.name = Arkycit Fal -block.crystalline-stone-wall.name = Kristályos-Kő Fal -block.red-ice-wall.name = Vörös-Jég Fal -block.red-stone-wall.name = Vörös-Kő Fal -block.red-diamond-wall.name = Vörös Gyémánt Fal -block.redweed.name = Vörös Fű -block.pur-bush.name = Lila Bokor -block.yellowcoral.name = Sárga Korall -block.carbon-boulder.name = Szén Szikla -block.ferric-boulder.name = Vasas Szikla -block.beryllic-boulder.name = Berilliumos Szikla -block.yellow-stone-boulder.name = Sárga Kő Szikla -block.arkyic-boulder.name = Arkycites Szikla -block.crystal-cluster.name = Kristály Fürt -block.vibrant-crystal-cluster.name = Vibráló Kristály Fürt -block.crystal-blocks.name = Kristály Blokkok -block.crystal-orbs.name = Kristály Gömbök -block.crystalline-boulder.name = Kristályos Szikla -block.red-ice-boulder.name = Vörös Jég Szikla -block.rhyolite-boulder.name = Riolit Szikla -block.red-stone-boulder.name = Vörös Kő Szikla -block.graphitic-wall.name = Grafit Fal -block.silicon-arc-furnace.name = Szilícium Ívkemence +block.yellow-stone.name = Sárga kő +block.carbon-stone.name = Szénkő +block.ferric-stone.name = Vasas kő +block.ferric-craters.name = Vasas kráterek +block.beryllic-stone.name = Berilliumos kő +block.crystalline-stone.name = Kristályos kő +block.crystal-floor.name = Kristálytalaj +block.yellow-stone-plates.name = Sárga kőlemezek +block.red-stone.name = Vörös kő +block.dense-red-stone.name = Sűrű vörös kő +block.red-ice.name = Vörös jég +block.arkycite-floor.name = Arkicit +block.arkyic-stone.name = Arkicites kő +block.rhyolite-vent.name = Riolitkürtő +block.carbon-vent.name = Szénkürtő +block.arkyic-vent.name = Arkicites kürtő +block.yellow-stone-vent.name = Sárgakő-kürtő +block.red-stone-vent.name = Vöröskő-kürtő +block.crystalline-vent.name = Kristályos kürtő +block.redmat.name = Vörös padló +block.bluemat.name = Kék padló +block.core-zone.name = Támaszpontzóna +block.regolith-wall.name = Regolitfal +block.yellow-stone-wall.name = Sárgakő-fal +block.rhyolite-wall.name = Riolit fal +block.carbon-wall.name = Szénfal +block.ferric-stone-wall.name = Vasaskő-fal +block.beryllic-stone-wall.name = Berilliumoskő-fal +block.arkyic-wall.name = Arkicites fal +block.crystalline-stone-wall.name = Kristályos kőFal +block.red-ice-wall.name = Vörösjég-fal +block.red-stone-wall.name = Vöröskő-fal +block.red-diamond-wall.name = Vörösgyémánt-fal +block.redweed.name = Vörös fű +block.pur-bush.name = Lila bokor +block.yellowcoral.name = Sárga korall +block.carbon-boulder.name = Szén szikla +block.ferric-boulder.name = Vasas szikla +block.beryllic-boulder.name = Berilliumos szikla +block.yellow-stone-boulder.name = Sárgakő-szikla +block.arkyic-boulder.name = Arkicites szikla +block.crystal-cluster.name = Kristályfürt +block.vibrant-crystal-cluster.name = Vibráló kristályfürt +block.crystal-blocks.name = Kristályblokkok +block.crystal-orbs.name = Kristálygömbök +block.crystalline-boulder.name = Kristályos szikla +block.red-ice-boulder.name = Vörösjég-szikla +block.rhyolite-boulder.name = Riolit szikla +block.red-stone-boulder.name = Vöröskő-szikla +block.graphitic-wall.name = Grafit fal +block.silicon-arc-furnace.name = Szilícium ívkemence block.electrolyzer.name = Elektrolizátor -block.atmospheric-concentrator.name = Atmoszferikus Sűrítő -block.oxidation-chamber.name = Oxidációs Kamra -block.electric-heater.name = Elektromos Fűtőtest -block.slag-heater.name = Salakos Fűtőtest -block.phase-heater.name = Tóritkvarcos Fűtőtest -block.heat-redirector.name = Hő Elvezető -block.heat-router.name = Hő Elosztó -block.slag-incinerator.name = Salakégető Kemence -block.carbide-crucible.name = Karbid Olvasztó -block.slag-centrifuge.name = Salak Centrifuga -block.surge-crucible.name = Elektrometál Olvasztó -block.cyanogen-synthesizer.name = Cianogén Szintetizáló -block.phase-synthesizer.name = Tóritkvarc Szintetizáló +block.atmospheric-concentrator.name = Atmoszferikus sűrítő +block.oxidation-chamber.name = Oxidációs kamra +block.electric-heater.name = Elektromos fűtőtest +block.slag-heater.name = Salakos fűtőtest +block.phase-heater.name = Tóritkvarcos fűtőtest +block.heat-redirector.name = Hőelvezető +block.heat-router.name = Hőelosztó +block.slag-incinerator.name = Salakégető kemence +block.carbide-crucible.name = Karbidolvasztó +block.slag-centrifuge.name = Salakcentrifuga +block.surge-crucible.name = Elektrometál-olvasztó +block.cyanogen-synthesizer.name = Diciánszintetizáló +block.phase-synthesizer.name = Tóritkvarc-szintetizáló block.heat-reactor.name = Hőerőmű -block.beryllium-wall.name = Berillium Fal -block.beryllium-wall-large.name = Nagy Berillium Fal -block.tungsten-wall.name = Volfrám Fal -block.tungsten-wall-large.name = Nagy Volfrám Fal -block.blast-door.name = Robbanásbiztos Ajtó -block.carbide-wall.name = Karbid Fal -block.carbide-wall-large.name = Nagy Karbid Fal -block.reinforced-surge-wall.name = Megerősített Elektrometál Fal -block.reinforced-surge-wall-large.name = Nagy Megerősített Elektrometál Fal -block.shielded-wall.name = Álcázott Fal +block.beryllium-wall.name = Berilliumfal +block.beryllium-wall-large.name = Nagy berilliumfal +block.tungsten-wall.name = Volfrámfal +block.tungsten-wall-large.name = Nagy volfrámfal +block.blast-door.name = Robbanásbiztos ajtó +block.carbide-wall.name = Karbidfal +block.carbide-wall-large.name = Nagy karbidfal +block.reinforced-surge-wall.name = Megerősített elektrometálfal +block.reinforced-surge-wall-large.name = Nagy megerősített elektrometálfal +block.shielded-wall.name = Pajzzsal védett fal block.radar.name = Radar -block.build-tower.name = Építő Torony -block.regen-projector.name = Épület Regenerátor -block.shockwave-tower.name = Sokkhullám Torony -block.shield-projector.name = Pajzs Generátor -block.large-shield-projector.name = Nagy Pajzs Generátor -block.armored-duct.name = Páncélozott Szállítószalag -block.overflow-duct.name = Túlfolyó Szállítószalag -block.underflow-duct.name = Alul-folyó Szállítószalag -block.duct-unloader.name = Kirakodó Szállítószalag -block.surge-conveyor.name = Elektrometál Szállítószalag -block.surge-router.name = Elektrometál Elosztó -block.unit-cargo-loader.name = Egység Rakomány Berakodó -block.unit-cargo-unload-point.name = Egység Rakomány Kirakodó Pont -block.reinforced-pump.name = Megerősített Szivattyú -block.reinforced-conduit.name = Megerősített Csővezeték -block.reinforced-liquid-junction.name = Megerősített Folyadék Átkötés -block.reinforced-bridge-conduit.name = Megerősített Csővezeték Híd -block.reinforced-liquid-router.name = Megerősített Folyadék Elosztó -block.reinforced-liquid-container.name = Megerősített Folyadék Konténer -block.reinforced-liquid-tank.name = Megerősített Folyadék Tartály -block.beam-node.name = Sugár Csomópont -block.beam-tower.name = Sugár Torony -block.beam-link.name = Sugár Hálózat -block.turbine-condenser.name = Kondenzációs Turbina -block.chemical-combustion-chamber.name = Kémiai Égető Kamra -block.pyrolysis-generator.name = Pirolízis Erőmű +block.build-tower.name = Építőtorony +block.regen-projector.name = Regeneráló kivetítő +block.shockwave-tower.name = Sokkhullámtorony +block.shield-projector.name = Pajzskivetítő +block.large-shield-projector.name = Nagy pajzskivetítő +block.armored-duct.name = Páncélozott szállítószalag +block.overflow-duct.name = Túlcsorduló szállítószalag +block.underflow-duct.name = Alulcsorduló szállítószalag +block.duct-unloader.name = Szállítószalag-kirakodó +block.surge-conveyor.name = Elektrometál-szállítószalag +block.surge-router.name = Elektrometál-elosztó +block.unit-cargo-loader.name = Egységrakomány-berakodó +block.unit-cargo-unload-point.name = Egységrakomány-kirakodó pont +block.reinforced-pump.name = Megerősített szivattyú +block.reinforced-conduit.name = Megerősített csővezeték +block.reinforced-liquid-junction.name = Megerősített folyadékátkötés +block.reinforced-bridge-conduit.name = Megerősített csővezetékhíd +block.reinforced-liquid-router.name = Megerősített folyadékelosztó +block.reinforced-liquid-container.name = Megerősített folyadékkonténer +block.reinforced-liquid-tank.name = Megerősített folyadéktartály +block.beam-node.name = Sugárcsomópont +block.beam-tower.name = Sugártorony +block.beam-link.name = Sugárhálózat +block.turbine-condenser.name = Kondenzációs turbina +block.chemical-combustion-chamber.name = Kémiai égetőkamra +block.pyrolysis-generator.name = Pirolíziserőmű block.vent-condenser.name = Vízleválasztó block.cliff-crusher.name = Sziklazúzó -block.plasma-bore.name = Plazma Vágó -block.large-plasma-bore.name = Nagy Plazma Vágó +block.plasma-bore.name = Plazmafúró +block.large-plasma-bore.name = Nagy plazmafúró block.impact-drill.name = Ütvefúró -block.eruption-drill.name = Erupciós Fúró +block.eruption-drill.name = Kitörési fúró block.core-bastion.name = Bástya block.core-citadel.name = Citadella block.core-acropolis.name = Akropolisz -block.reinforced-container.name = Megerősített Konténer -block.reinforced-vault.name = Megerősített Tároló +block.reinforced-container.name = Megerősített konténer +block.reinforced-vault.name = Megerősített tároló block.breach.name = Breach block.sublimate.name = Sublimate block.titan.name = Titan @@ -1809,39 +1809,39 @@ block.disperse.name = Disperse block.afflict.name = Afflict block.lustre.name = Lustre block.scathe.name = Scathe -block.tank-refabricator.name = Tank Újratervező -block.mech-refabricator.name = Mech Újratervező -block.ship-refabricator.name = Repülőgép Újratervező -block.tank-assembler.name = Tank Összeszerelő -block.ship-assembler.name = Repülőgép Összeszerelő -block.mech-assembler.name = Mech Összeszerelő -block.reinforced-payload-conveyor.name = Megerősített Rakomány Szállítószalag -block.reinforced-payload-router.name = Megerősített Rakomány Elosztó -block.payload-mass-driver.name = Rakomány Tömegmozgató -block.small-deconstructor.name = Kis Lebontó -block.canvas.name = Vászon Kijelző -block.world-processor.name = Világ Processzor -block.world-cell.name = Világ Cella -block.tank-fabricator.name = Tank Gyár -block.mech-fabricator.name = Mech Gyár -block.ship-fabricator.name = Repülőgép Gyár -block.prime-refabricator.name = Elsődleges Újratervező -block.unit-repair-tower.name = Egység Javító Torony +block.tank-refabricator.name = Tankújratervező +block.mech-refabricator.name = Mechújratervező +block.ship-refabricator.name = Repülőgép-újratervező +block.tank-assembler.name = Tankösszeszerelő +block.ship-assembler.name = Hajó-összeszerelő +block.mech-assembler.name = Mechösszeszerelő +block.reinforced-payload-conveyor.name = Megerősített rakományszállító-szalag +block.reinforced-payload-router.name = Megerősített rakományelosztó +block.payload-mass-driver.name = Rakomány-tömegmozgató +block.small-deconstructor.name = Lebontó +block.canvas.name = Vászon +block.world-processor.name = Világprocesszor +block.world-cell.name = Világcella +block.tank-fabricator.name = Tankgyártó +block.mech-fabricator.name = Mechgyártó +block.ship-fabricator.name = Repülőgépgyártó +block.prime-refabricator.name = Elsődleges újratervező +block.unit-repair-tower.name = Egységjavító torony block.diffuse.name = Diffuse -block.basic-assembler-module.name = Alap Összeszerelő Modul +block.basic-assembler-module.name = Alapvető összeszerelő modul block.smite.name = Smite block.malign.name = Malign -block.flux-reactor.name = Fluxus Reaktor -block.neoplasia-reactor.name = Neoplázia Reaktor +block.flux-reactor.name = Fluxusreaktor +block.neoplasia-reactor.name = Neopláziareaktor block.switch.name = Kapcsoló block.micro-processor.name = Mikroprocesszor -block.logic-processor.name = Logikai Processzor -block.hyper-processor.name = Hiper Processzor -block.logic-display.name = Logikai Kijelző -block.large-logic-display.name = Nagy Logikai Kijelző -block.memory-cell.name = Memória Cella -block.memory-bank.name = Memória Bank +block.logic-processor.name = Logikai processzor +block.hyper-processor.name = Hiperprocesszor +block.logic-display.name = Logikai kijelző +block.large-logic-display.name = Nagy logikai kijelző +block.memory-cell.name = Memóriacella +block.memory-bank.name = Memóriabank team.malis.name = Lila team.crux.name = Piros @@ -1852,169 +1852,169 @@ team.blue.name = Kék hint.skip = Kihagyás hint.desktopMove = Használd a [accent][[WASD][] gombokat a mozgáshoz. -hint.zoom = Használd az egér [accent]Görgőjét[] a zoomhoz.. -hint.desktopShoot = Használd a [accent]Bal-Egérgomb[]ot a lövéshez. -hint.depositItems = A nyersanyagokat áthelyezheted húzással a drónból a Támaszpontba. +hint.zoom = [accent]Görgess[] a nagyításhoz és kicsinyítéshez. +hint.desktopShoot = Használd a [accent]bal egérgombot[] a lövéshez. +hint.depositItems = A nyersanyagokat húzással helyezheted át a drónból a támaszpontba. hint.respawn = Ahhoz, hogy drónként újraéledj, nyomd meg a [accent][[V][] gombot. -hint.respawn.mobile = Átvetted az irányítást egy egység, vagy lövegtorony felett. Ahhoz, hogy drónként újraéledj, [accent]érintsd meg az avatárt a bal felső sarokban.[] -hint.desktopPause = Nyomd meg a [accent][[Szóköz][]t, hogy szüneteltesd, vagy folytasd a játékot. -hint.breaking = A [accent]Jobb-Egérgomb[]ot nyomva kijelölheted az útban lévő épületeket a lebontásukhoz. -hint.breaking.mobile = Használd a \ue817 [accent]Kalapács[] gombot jobb lent és töröld vele az útban lévő épületeket.\n\nTartsd lenyomva az ujjad és húzd, hogy nagyobb területet tudj kijelölni. -hint.blockInfo = Egy épület információinak megtekintéséhez válaszd ki az épületet az [accent]Építés menüben[], majd válaszd a [accent][[?][] gombot a jobb oldalon. -hint.derelict = Az [accent]Elhagyatott[] szerkezetek régi bázisok maradványai, amelyek már nem működnek.\n\nEzeket a maradványokat le lehet [accent]bontani[] nyersanyagokért, vagy megjavítani őket. -hint.research = Használd a \ue875 [accent]Fejlesztési Fa[] gombot, hogy felfedezz új technológiákat. -hint.research.mobile = Használd a \ue875 [accent]Fejlesztési Fa[] gombot a \ue88c [accent]Menü[]ben, hogy felfedezz új technológiákat. -hint.unitControl = Nyomd le a [accent][[Bal-CTRL][] gombot és kattints a [accent]Jobb-Egérgomb[]al az irányítani kívánt szövetséges egységre, vagy -lövegtoronyra, hogy átvedd fölötte az irányítást. -hint.unitControl.mobile = [accent][[Dupla koppintás][]sal átveheted az irányítást szövetséges egységek, vagy -lövegtornyok felett. -hint.unitSelectControl = Az egységek irányításához lépj be a [accent]Parancs Mód[]ba, úgy, hogy nyomd hosszan a [accent]Bal-SHIFT[] gombot.\nParancs Módban az egységek kijelöléséhez kattints és húzd az egeret. A [accent]Jobb-Egérgomb[]-al kattints az egységek mozgatásához egy helyszínre, vagy az ellenséges célpontra. -hint.unitSelectControl.mobile = Az egységek irányításához lépj be a [accent]Parancs Mód[]ba, úgy, hogy nyomd meg a [accent]Parancs Mód[] gombot a bal alsó sarokban.\nParancs módban az egységek kiválasztásához érintsd meg a kijelzőt és húzással jelöld ki az egységeket. Koppints az egységek mozgatásához egy helyszínre, vagy ellenséges az célpontra. -hint.launch = Ha elegendő nyersanyagot gyűjtöttél össze, akkor [accent]Indítsd[] el a Támaszpontot a következő szektorba, úgy, hogy megnyitod a \ue827 [accent]Bolygó térkép[]et a jobb alsó sarokban, és átpásztázol az új helyszínre. -hint.launch.mobile = Ha elegendő nyersanyagot gyűjtöttél össze, akkor [accent]Indítsd[] el a Támaszpontot egy közeli szektorba, úgy, hogy kiválasztasz egy szektort a \ue88c [accent]Menü[]ben a \ue827 [accent]Bolygó térkép[]ről. -hint.schematicSelect = Az [accent][[F][] nyomva tartásával egyszerre több épületet is kijelölhetsz és másolhatsz.\n\nAz egér [accent][[Görgővel][] viszont, csak egy épületet tudsz kijelölni és lemásolni. -hint.rebuildSelect = Tartsd nyomva a [accent][[B][] és húzással jelöld ki a megsemmisített épületterveket.\nEz automatikusan újraépíti őket. -hint.rebuildSelect.mobile = Válaszd a \ue874 másolás gombot, majd koppints \ue80f újjáépítés gombra, és húzd a megsemmisült épülettervek kijelöléséhez.\nEz automatikusan újraépíti őket. -hint.conveyorPathfind = Tartsd nyomva a [accent][[Bal-CTRL][] gombot a szállítószalagok lerakása közben, hogy a játék útvonalat generáljon. -hint.conveyorPathfind.mobile = Engedélyezd az \ue844 [accent]Átlós Mód[]ot és tegyél le egyszerre több szállítószalagot, hogy a játék útvonalat generáljon. -hint.boost = Tartsd nyomva a [accent][[Bal-SHIFT][] gombot, hogy átrepülj az akadályok felett.\n\nErre nem minden földi egység képes. -hint.payloadPickup = A [accent][[[] gombbal kis épületeket, vagy egységeket emelhetsz fel. -hint.payloadPickup.mobile = [accent]Tartsd nyomva az ujjad[] egy kis épületen, vagy egységen, hogy felemeld. -hint.payloadDrop = A [accent]][] megnyomásával lerakhatod a rakományodat. -hint.payloadDrop.mobile = [accent]Tartsd hosszan az ujjad[] egy üres területen, hogy letedd a rakományodat. -hint.waveFire = A [accent]Wave[] lövegtornyok, ha Víz van bennük, automatikusan eloltják a közeli tüzeket. -hint.generator = A \uf879 [accent]Belső-égetésű Erőmű[] Szenet éget, és átadja az Áramot a vele érintkező épületeknek.\n\nÁramot nagyobb távolságra is szállíthatsz \uf87f [accent]Villanyoszlop[]ok segítségével. -hint.guardian = Az [accent]Őrző[]knek páncélja van. A gyenge lövedékeknek, mint a [accent]Réz[], vagy az [accent]Ólom[] [scarlet]nincs hatásuk[] az Őrző páncéljára.\n\nHasználj magasabb szintű lövegtornyokat, vagy \uf835 [accent]Grafit[] lövedéket a \uf861Duo/\uf859Salvo lövegtornyokban, hogy leszedd az Őrzőket. -hint.coreUpgrade = A Támaszpontot fejlesztheted, ha [accent]magasabb szintű Támaszpontot teszel rá[].\n\nHelyezz egy [accent]Alapítvány[] Támaszpontot a [accent]Szilánk[] Támaszpontra. Figyelj rá, hogy ne legyenek az új Támaszpont területén épületek. -hint.presetLaunch = A szürke [accent]hadjárat szektorok[]ba, amilyen például a [accent]Fagyott Erdő[], bárhonnan kilőhetsz. Nem kell szomszédos területtel rendelkezned.\n\nA [accent]számozott szektorok[], mint ez is, [accent]opcionálisak[]. +hint.respawn.mobile = Átvetted az irányítást egy egység vagy épület felett. Ahhoz, hogy drónként újraéledj, [accent]koppints a profilképre a bal felső sarokban.[] +hint.desktopPause = Nyomd meg a [accent][[Szóközt][] a játék szüneteltetéséhez vagy folytatásához. +hint.breaking = [accent]Jobb egérgombbal[] és húzással lebonthatod a blokkokat. +hint.breaking.mobile = Használd a jobb alsó sarokban lévő  [accent]kalapács[] gombot a blokkok törléséhez.\n\nTartsd lenyomva az ujjad és húzd, hogy nagyobb területet tudj kijelölni. +hint.blockInfo = Egy blokk információinak megtekintéséhez válaszd ki az épületet az [accent]építési menüben[], majd válaszd a [accent][[?][] gomb jobb oldalt. +hint.derelict = Az [accent]elhagyatott[] szerkezetek régi bázisok maradványai, amelyek már nem működnek.\n\nEzeket az épületeket le lehet [accent]bontani[] nyersanyagokért, vagy meg is lehet javítani őket. +hint.research = Használd a  [accent]Fejlesztési fa[] gombot, hogy új technológiákat fedezz fel. +hint.research.mobile = Használd a  [accent]Fejlesztési fa[] gombot a  [accent]menüben[], hogy új technológiákat fedezz fel. +hint.unitControl = Nyomd le a [accent][[bal Ctrl][] gombot, és kattints [accent]jobb egérgombbal[] a baráti egység vagy lövegtorony irányításához. +hint.unitControl.mobile = [accent][[Dupla koppintással][] irányíthatók kézileg a szövetséges egységek vagy lövegtornyok. +hint.unitSelectControl = Az egységek irányításához lépj be [accent]parancs módba[] a [accent]bal Shift[] lenyomva tartásával.\nParancs módban az egységek kijelöléséhez kattints, és húzd az egeret. A [accent]jobb egérgombbal[] küldd az egységeket a helyszínre vagy a célponthoz. +hint.unitSelectControl.mobile = Az egységek irányításához lépj be [accent]parancs módba[] a bal alsó sarokban lévő [accent]parancs[] gombbal.\nParancs módban az egységek kiválasztásához érintsd meg a kijelzőt és húzással jelöld ki az egységeket. Koppintással küldd az egységeket a helyszínre vagy a célponthoz. +hint.launch = Ha elegendő nyersanyagot gyűjtöttél össze, akkor [accent]lődd ki[] a támaszpontot a következő szektorba, úgy, hogy megnyitod a  [accent]Bolygótérképet[] a jobb alsó sarokban, és átforgatod az új helyszínre. +hint.launch.mobile = Ha elegendő nyersanyagot gyűjtöttél össze, akkor [accent]lődd ki[] el a támaszpontot egy közeli szektorba, úgy, hogy kiválasztasz egy szektort a  [accent]Menüben[] a  [accent]Bolygótérképről[]. +hint.schematicSelect = Tartsd nyomja az [accent][[F][] gombot több épület kijelöléséhez és másolásához.\n\n[accent][[Középső kattintással][] egy adott blokktípus másolható. +hint.rebuildSelect = Tartsd nyomva a [accent][[B][] gombot és húzással jelöld ki a megsemmisített blokkterveket.\nEz automatikusan újraépíti őket. +hint.rebuildSelect.mobile = Válaszd a  másolás gombot, majd koppints az  újjáépítés gombra, és húzd a megsemmisült blokktervek kijelöléséhez.\nEz automatikusan újraépíti őket. +hint.conveyorPathfind = Tartsd nyomva a [accent][[bal Ctrl][] gombot a szállítószalagok lerakása közben, hogy a játék útvonalat állítson elő. +hint.conveyorPathfind.mobile = Engedélyezd az  [accent]átlós módot[], és tegyél le egyszerre több szállítószalagot, hogy a játék útvonalat állítson elő. +hint.boost = Tartsd nyomva a [accent][[bal Shift][] gombot, hogy átrepülj az akadályok felett.\n\nErre csak néhány földi egység képes. +hint.payloadPickup = Nyomd meg a [accent][[[] gombot a kis blokkok vagy egységek felemeléséhez. +hint.payloadPickup.mobile = [accent]Koppints és tartsd lenyomva az ujjad[] egy kis blokk vagy egység felemeléséhez. +hint.payloadDrop = Nyomd le a [accent]][] gombot a rakomány lerakásához. +hint.payloadDrop.mobile = [accent]Koppints és tartsd lenyomva az ujjad[] egy üres területen a rakomány lerakásához. +hint.waveFire = A vizet lőszerként használó [accent]Wave[] lövegtornyok automatikusan eloltják a közeli tüzeket. +hint.generator = Az  [accent]égetőerőmű[] szenet éget, és áramot ad át a vele érintkező épületeknek.\n\nAz áramszállítás távolsága [accent]villanyoszlopokkal[] növelhető. +hint.guardian = Az [accent]Őrzők[] páncélozottak. A gyenge lövedékek, mint a [accent]réz[] vagy az [accent]ólom[] [scarlet]nem hatásosak[] az Őrző páncéljával szemben.\n\nHasználj magasabb szintű lövegtornyokat, vagy  [accent]grafitot[] a Duo/Salvo lövegtornyokban, hogy leszedd az Őrzőket. +hint.coreUpgrade = A támaszpont úgy fejleszthető, hogy [accent]magasabb szintű támaszpontot teszel rá[].\n\nHelyezz egy  [accent]Alapítvány[] támaszpontot a  [accent]Szilánk[] támaszpontra. Figyelj rá, hogy ne legyenek az új támaszpont területén épületek. +hint.presetLaunch = A szürke [accent]landolási zónát tartalmazó szektorokba[], amilyen például a [accent]Fagyott erdő[], bárhonnan kilőhetsz. Nem szükséges hozzá szomszédos területet elfoglalnod.\n\nA [accent]számozott szektorok[], mint ez is, [accent]nem kötelezők[]. hint.presetDifficulty = Ebben a szektorban [scarlet]magas az ellenséges fenyegetettségi szint[].\nAz ilyen szektorokba való indulás [accent]nem ajánlott[] megfelelő technológia és felkészülés nélkül. -hint.coreIncinerate = Ha a Támaszpontodban egy nyersanyag elérte a maximumot, a beérkező ilyen nyersanyagaid azonnal [accent]megsemmisítésre kerülnek[]. -hint.factoryControl = Egy egységgyár [accent]kimeneti célpontjának[] beállításához Parancs Módban kattints egy gyárépületre, majd kattints a Jobb-Egérgombbal egy helyre.\nAz előállított egységek automatikusan odamennek. -hint.factoryControl.mobile = Egy egységgyár [accent]kimeneti célpontjának[] beállításához Parancs Módban koppints egy gyárépületre, majd koppints egy helyre.\nAz előállított egységek automatikusan odamennek. +hint.coreIncinerate = Ha a támaszpont egy nyersanyagból elérte a maximumot, a beérkező további nyersanyagok azonnal [accent]megsemmisítésre kerülnek[]. +hint.factoryControl = Egy egységgyár [accent]kimeneti célpontjának[] beállításához kattints parancs módban egy gyárépületre, majd kattints jobb egérgombbal egy helyre.\nAz előállított egységek automatikusan odamennek. +hint.factoryControl.mobile = Egy egységgyár [accent]kimeneti célpontjának[] beállításához koppints parancs módban egy gyárépületre, majd koppints egy helyre.\nAz előállított egységek automatikusan odamennek. -gz.mine = Menj a földön lévő \uf8c4 [accent]Rézérc[] közelébe, és kattints a bányászat megkezdéséhez. -gz.mine.mobile = Menj a földön lévő \uf8c4 [accent]Rézérc[] közelébe, és koppints a bányászat megkezdéséhez. -gz.research = Nyisd meg a \ue875 Fejlesztési fát.\nFejleszd ki a \uf870 [accent]Mechanikus Fúró[]t, majd válaszd ki a jobb alsó sarokban lévő menüből.\nKattints egy Rézfoltra, hogy elhelyezd. -gz.research.mobile = Nyisd meg a \ue875 Fejlesztési fát.\nFejleszd ki a \uf870 [accent]Mechanikus Fúró[]t, majd válaszd ki a jobb alsó sarokban lévő menüből.\nKattints egy Rézfoltra, hogy elhelyezd.\n\nNyomd meg a \ue800 [accent]Pipa[]t a jobb alsó sarokban a megerősítéshez. -gz.conveyors = Fejleszd ki és építs \uf896 [accent]Szállítószalag[]ot, hogy a kitermelt nyersanyagokat\neljuttasd a fúróktól/vágóktól a Támaszpontba.\n\nKattints és húzd az egeret bármely irányba, hogy hosszabb Szállítószalagot készíthess.\n[accent]Görgővel[] a szállítás irányát tudod megváltoztatni. -gz.conveyors.mobile = Fejleszd ki és építs \uf896 [accent]Szállítószalag[]ot, hogy a kitermelt nyersanyagokat\neljuttasd a fúróktól/vágóktól a Támaszpontba.\n\nTartsd rajta az ujjad egy másodpercig, és húzd bármely irányba, hogy hosszabb Szállítószalagot készíthess. -gz.drills = Bővítsd a bányászati kapacitást.\nÉpíts több Mechanikus Fúrót.\nBányássz 100 Rezet. -gz.lead = Az \uf837 [accent]Ólom[] egy másik gyakran használt nyersanyag.\nÉpíts fúrókat az Ólom kitermelésére. -gz.moveup = \ue804 Menj tovább a többi feladatért. -gz.turrets = Fejleszd ki és építs 2 darab \uf861 [accent]Duo[] lövegtornyot, hogy védd a Támaszpontot.\nA Duo lövegtornyoknak szükségük van \uf838 [accent]lőszer[]re, amelyeket Szállítószalaggal juttathatsz el hozzájuk. -gz.duoammo = Lásd el a Duo lövegtornyokat [accent]Rézzel[]. Használj Szállítószalagot. -gz.walls = A [accent]Falak[] megakadályozhatják, hogy az épületekben károk keletkezzenek.\nÉpíts \uf8ae [accent]Réz Fal[]akat a lövegtornyok körül. +gz.mine = Menj a földön lévő  [accent]rézérc[] közelébe, és kattints a bányászat megkezdéséhez. +gz.mine.mobile = Menj a földön lévő  [accent]rézérc[] közelébe, és koppints a bányászat megkezdéséhez. +gz.research = Nyisd meg a  Fejlesztési fát.\nFejleszd ki a  [accent]Mechanikus fúrót[], majd válaszd ki a jobb alsó sarokban lévő menüből.\nKattints egy rézfoltra az elhelyezéséhez. +gz.research.mobile = Nyisd meg a  Fejlesztési fát.\nFejleszd ki a  [accent]Mechanikus fúrót[], majd válaszd ki a jobb alsó sarokban lévő menüből.\nKattints egy rézfoltra az elhelyezéséhez.\n\nA megerősítéshez nyomd meg a jobb alsó sarokban lévő  [accent]pipát[]. +gz.conveyors = Fejleszd ki, és építs  [accent]szállítószalagokat[], hogy a kitermelt\nnyersanyagokat eljuttasd a fúróktól a támaszpontba.\n\nKattints és húzd az egeret, hogy több szállítószalagot helyezz el.\nHasználd a [accent]görgőt[] a forgatáshoz. +gz.conveyors.mobile = Fejleszd ki, és építs  [accent]szállítószalagokat[], hogy a kitermelt\nnyersanyagokat eljuttasd a fúróktól a támaszpontba.\n\nTartsd lenyomva az ujjad és húzd el, hogy több szállítószalagot helyezz el. +gz.drills = Bővítsd a bányászati kapacitást.\nÉpíts több mechanikus fúrót.\nBányássz 100 rezet. +gz.lead = Az  [accent]ólom[] egy másik gyakran használt nyersanyag.\nÉpíts fúrókat az ólom kitermelésére. +gz.moveup =  Menj tovább a további utasításokért. +gz.turrets = Fejleszd ki, és építs 2  [accent]Duo[] lövegtornyot, hogy megvédd a támaszpontot.\nA Duo lövegtornyoknak  [accent]lőszerre[] van szükségük, mely szállítószalaggal juttatható el hozzájuk. +gz.duoammo = Szállítószalagok segítségével lásd el [accent]rézzel[] a Duo lövegtornyokat. +gz.walls = A [accent]falak[] megakadályozhatják, hogy az épületekben károk keletkezzenek.\nÉpíts  [accent]rézfalakat[] a lövegtornyok köré. gz.defend = Az ellenség közeledik, készülj fel a védekezésre. -gz.aa = A repülő egységeket nem lehet könnyen elintézni a hagyományos lövegtornyokkal.\nA \uf860 [accent]Scatter[] lövegtornyok kiváló légelhárítást biztosítanak, de szükségük van \uf837 [accent]Ólom[] lőszerre. -gz.scatterammo = Lásd el a Scatter lövegtornyokat [accent]Ólom[] lőszerrel. Használj Szállítószalagot. -gz.supplyturret = [accent]Lövegtorony ellátmány +gz.aa = A repülő egységeket nem lehet könnyen elintézni a hagyományos lövegtornyokkal.\nA  [accent]Scatter[] lövegtornyok kiváló légelhárítást biztosítanak, de lőszerként  [accent]ólomra[] van szükségük. +gz.scatterammo = Szállítószalagok segítségével lásd el  [accent]ólommal[] a Scatter lövegtornyokat. +gz.supplyturret = [accent]Lövegtorony ellátása gz.zone1 = Ez az ellenség leszállóhelye. gz.zone2 = Bármi, ami a hatósugarában épült, elpusztul, amikor egy hullám elindul. gz.zone3 = Egy hullám most kezdődik.\nKészülj fel. -gz.finish = Építs több lövegtornyot, bányássz több nyersanyagot\nés védekezz az ellenséges hullámok ellen, hogy [accent]elfoglald a szektort[]. +gz.finish = Építs több lövegtornyot, bányássz több nyersanyagot,\nés védekezz az ellenséges hullámok ellen, hogy [accent]elfoglald a szektort[]. -onset.mine = Kattints a Bal-Egérgommbal a \uf748 [accent]Berillium[]ra, hogy kibányászd a falakból.\n\nHasználd a [accent][[WASD] gombokat a mozgáshoz. -onset.mine.mobile = Koppints a \uf748 [accent]Berillium[]ra, hogy kibányászd a falakból. -onset.research = Nyisd meg a \ue875 Fejlesztési fát.\nFejleszd ki és építs egy \uf73e [accent]Kondenzációs Turbina[]t a kürtőn.\nEz [accent]Áram[]ot fog generálni. -onset.bore = Fejleszd ki és építs \uf741 [accent]Plazma Vágó[]t.\nEz automatikusan bányássza ki a nyersanyagokat a falakból. -onset.power = Ahhoz, hogy [accent]Áram[]mal lásd el a Plazma Fúrót, Fejleszd ki és építs \uf73d [accent]Sugár Csomópont[]okat.\nSegítségükkel összekötheted a Kondenzációs Turbinát a Plazma Vágóval. -onset.ducts = Fejleszd ki és építs \uf799 [accent]Szállítószalag[]ot, hogy a kitermelt nyersanyagokat eljuttasd a fúróktól/vágóktól a Támaszpontba\nKattints és húzd az egeret bármely irányba, hogy hosszabb Szállítószalagot készíthess.\n[accent]Görgővel[] a szállítás irányát tudod megváltoztatni. -onset.ducts.mobile = Fejleszd ki és építs \uf799 [accent]Szállítószalag[]ot, hogy a kitermelt nyersanyagokat\neljuttasd a fúróktól/vágóktól a Támaszpontba.\n\nTartsd rajta az ujjad egy másodpercig, és húzd bármely irányba, hogy hosszabb Szállítószalagot készíthess. -onset.moremine = Bővítsd a bányászati kapacitást.\nÉpíts több Plazma Vágót, használj Sugár Csomópontokat és Szállítószalagokat.\nBányássz 200 Berilliumot. -onset.graphite = Az összetettebb épületekhez szükség van \uf835 [accent]Grafit[]ra.\nÉpíts Plazma Vágókat, a Grafit kibányászásához. -onset.research2 = Kezdd el a [accent]Gyárak[] fejlesztését.\nFejleszd ki a \uf74d [accent]Sziklazúzó[]t és a \uf779 [accent]Szilícium Ívkemence[]t. -onset.arcfurnace = A Szilícium Ívkemencének szüksége lesz \uf834 [accent]Homok[]ra és \uf835 [accent]Grafit[]ra, hogy \uf82f [accent]Szilícium[]ot gyártson.\nTovábbá [accent]Áram[] is szükséges a működéséhez. -onset.crusher = Használj \uf74d [accent]Sziklazúzó[]t, hogy Homokot bányászhass. -onset.fabricator = Használd az [accent]egységeket[], hogy felfedezd a pályát, megvédd az épületeket, és megtámadhasd velük az ellenséget. Fejleszd ki és építs \uf6a2 [accent]Tank Gyár[]at. -onset.makeunit = Állíts elő egy egységet.\nHasználd a "?" gombot, hogy megnézd a gyártási követelményeit. -onset.turrets = Az egységek hatékonyak, de a [accent]lövegtornyok[] jobb védelmi képességeket biztosítanak, ha hatékonyan alkalmazod őket.\nÉpíts egy \uf6eb [accent]Breach[] lövegtornyot.\nA lövegtornyoknak szüksége van \uf748 [accent]lőszer[]re. -onset.turretammo = Lásd el a lövegtornyokat [accent]Berillium[] lőszerrel. -onset.walls = A [accent]Falak[] megakadályozhatják, hogy az épületekben károk keletkezzenek.\nÉpíts \uf6ee [accent]Berillium Fal[]akat a lövegtornyok körül. +onset.mine = Kattints bal egérgombbal a  [accent]berillium[] kibányászáshoz a falakból.\n\nA mozgáshoz használd a [accent][[WASD] gombokat. +onset.mine.mobile = Koppints a  [accent]berillium[] kibányászáshoz a falakból. +onset.research = Nyisd meg a  fejlesztési fát.\nFejleszd ki, és építs egy  [accent]kondenzációs turbinát[] a kürtőn.\nEz [accent]áramot[] fog termelni. +onset.bore = Fejleszd ki, és építs egy  [accent]plazmafúrót[].\nEz automatikusan bányássza ki a nyersanyagokat a falakból. +onset.power = Ahhoz, hogy [accent]árammal[] lásd el a plazmafúrót, fejleszd ki, és helyezz el egy  [accent]sugárcsomópontot[].\nSegítségükkel összekötheted a kondenzációs turbinát a plazmafúróval. +onset.ducts = Fejleszd ki, és építs  [accent]szállítószalagot[], hogy a kitermelt nyersanyagokat eljuttasd a plazmafúrótól a támaszpontba.\nKattints, és húzd az egeret több szállítószalag elhelyezéséhez.\nHasználd a [accent]görgőt[] a forgatáshoz. +onset.ducts.mobile = Fejleszd ki, és építs  [accent]szállítószalagot[], hogy a kitermelt nyersanyagokat eljuttasd a plazmafúrótól a támaszpontba.\n\nTartsd lenyomva az ujjad és húzd el, hogy több szállítószalagot helyezz el. +onset.moremine = Bővítsd a bányászati kapacitást.\nHelyezz el több plazmavágót, és a támogatásukhoz használj sugárcsomópontokat és szállítószalagokat.\nBányássz 200 berilliumot. +onset.graphite = Az összetettebb épületekhez  [accent]grafit[] szükséges.\nÉpíts plazmavágókat a grafit kibányászásához. +onset.research2 = Kezdd el a [accent]gyárak[] fejlesztését.\nFejleszd ki a  [accent]sziklazúzót[] és a  [accent]szilícium ívkemencét[]. +onset.arcfurnace = A Szilícium ívkemencének  [accent]homokra[] és  [accent]grafitra[] van szüksége, hogy  [accent]szilíciumot[] gyártson.\nTovábbá [accent]áram[] is szükséges a működéséhez. +onset.crusher = Használj  [accent]sziklazúzókat[], hogy homokot bányász. +onset.fabricator = Használd az [accent]egységeket[], hogy felfedezd a pályát, megvédd az épületeket, és megtámadhasd velük az ellenséget. Fejleszd ki, és helyezz el egy  [accent]tankgyárat[]. +onset.makeunit = Állíts elő egy egységet.\nHasználd a „?” gombot, hogy megnézd a kiválasztott gyár követelményeit. +onset.turrets = Az egységek hatékonyak, de hatásosan alkalmazva a [accent]lövegtornyok[] jobb védelmi képességeket biztosítanak.\nHelyezz el egy  [accent]Breach[] lövegtornyot.\nA lövegtornyoknak  [accent]lőszerre[] van szüksége. +onset.turretammo = Szállítótalagok használatával lásd el a lövegtornyokat [accent]berillium[] lőszerrel. +onset.walls = A [accent]falak[] megakadályozhatják, hogy az épületekben károk keletkezzenek.\nÉpíts  [accent]Berillium falakat[] a lövegtornyok körül. onset.enemies = Az ellenség közeledik, készülj fel a védekezésre. onset.defenses = [accent]Állíts fel védelmet:[lightgray] {0} -onset.attack = Az ellenség most sebezhető. Ellentámadásra fel. -onset.cores = Új Támaszpont csak a [accent]Támaszpont Csempé[]re építhető.\nAz új Támaszpontok előretolt bázisként működnek, és megosztják a nyersanyagkészletüket más Támaszpontokkal.\nÉpíts egy új \uf725 Támaszpontot. -onset.detect = Az ellenség 2 percen belül észrevesz téged.\nÁllíts fel védelmet, bányássz és termelj. -onset.commandmode = Tartsd nyomva a [accent]shift[] gombot, hogy belépj a [accent]Parancs Mód[]ba.\n[accent]Bal egérgomb lenyomásával és húzással[] tudod kijelölni az egységeket.\nKattints [accent]Jobb egérgomb[]al egy pontra, vagy ellenségre, hogy a kiválasztott egységeket mozgásra, vagy támadásra utasítsd. -onset.commandmode.mobile = Nyomd meg a [accent]Parancs[] gombot, hogy belépj a [accent]Parancs Mód[]ba.\nTartsd a kijelzőn az ujjad, majd [accent]húzd[] azt az egységek kiválasztásához.\n[accent]Koppints[] egy pontra, vagy ellenségre, hogy a kiválasztott egységeket mozgásra, vagy támadásra utasítsd. -aegis.tungsten = Volfrámot [accent]Ütvefúró[]val tudsz bányászni.\nEnnek az épületnek szüksége van [accent]Víz[]re és [accent]Áram[]ra. +onset.attack = Az ellenség most sebezhető. Indítsd ellentámadást. +onset.cores = Új támaszpont csak a [accent]támaszpontcsempére[] helyezhető.\nAz új támaszpontok előretolt bázisként működnek, és megosztják a nyersanyagkészletüket más támaszpontokkal.\nHelyezz el egy  támaszpontot. +onset.detect = Az ellenség 2 percen belül észrevesz téged.\nÁllíts fel védelmet, bányászatot és termelést. +onset.commandmode = Tartsd nyomva a [accent]Shift[] gombot, hogy [accent]parancs módba[] lépj.\n[accent]Bal egérgombbal és húzással[] lehet egységeket kijelölni.\n[accent]Jobb egérgombbal[] utasíthatók az egységek mozgásra vagy támadásra. +onset.commandmode.mobile = Nyomd meg a [accent]parancs gombot[], hogy [accent]parancs módba[] lépj.\nTartsd nyomva az ujjad, majd [accent]húzd[] az egységek kiválasztásához.\n[accent]Koppintással[] utasíthatók az egységek mozgásra vagy támadásra. +aegis.tungsten = Volfrámot [accent]ütvefúróval[] lehet bányászni.\nEnnek az épületnek [accent]vízre[] és [accent]áramra[] van szüksége. -split.pickup = Egyes blokkok a Támaszpont drónjával felvehetőek.\nVegyél fel egy [accent]Konténer[]t és helyezd bele egy [accent]Rakomány Csomagoló[]ba.\n(Az alapértelmezett gombok [ és ] a felvételhez és a lerakáshoz.) -split.pickup.mobile = Egyes blokkok a Támaszpont drónjával felvehetőek.\nVegyél fel egy [accent]Konténer[]t és helyezd bele egy [accent]Rakomány Csomagoló[]ba.\nAhhoz felvegyél, vagy lerakj valamit koppints rá hosszan) -split.acquire = Az egységek építéséhez Volfrámot kell szerezned. -split.build = Az egységeket a fal másik oldalára kell eljuttatni.\nÉpíts 2 [accent]Rakomány Tömegmozgató[]t egyet-egyet a fal mindkét oldalán\nÁllítsd be a szállítási kapcsolatukat úgy, hogy kiválasztod az egyiket, majd kiválasztod a másikat. -split.container = Hasonlít a Konténerre, csak egységek szállítására is alkalmas a [accent]Rakomány Tömegmozgató[]val.\nÉpíts egy egységgyárat egy tömegmozgató mellé, hogy feltöltsd őket, majd küldd át őket a falon, hogy megtámadják az ellenséges bázist. +split.pickup = Egyes blokkok a támaszpont drónjával is felvehetők.\nVedd fel ezt a [accent]konténert[] és helyezd egy [accent]rakománycsomagolóba[].\n(A felvétel és lerakás alapértelmezett gombjai: [[ és ].) +split.pickup.mobile = Egyes blokkok a támaszpont drónjával is felvehetők.\nVedd fel ezt a [accent]konténert[] és helyezd egy [accent]rakománycsomagolóba[].\n(A felvételhez és lerakáshoz nyomd meg hosszan.) +split.acquire = Az egységek építéséhez volfrámot kell szerezned. +split.build = Az egységeket a fal másik oldalára kell eljuttatni.\nÉpíts két [accent]rakomány-tömegmozgatót[], egyet-egyet a fal mindkét oldalán.\nÁllítsd be a szállítási kapcsolatukat úgy, hogy kiválasztod az egyiket, majd kiválasztod a másikat. +split.container = A konténerekhez hasonlóan, az egységek is szállíthatók a [accent]rakomány-tömegmozgatóval[].\nÉpíts egy egységgyárat egy tömegmozgató mellé, hogy feltöltsd őket, majd küldd át őket a falon, hogy megtámadják az ellenséges bázist. -item.copper.description = Széleskörűen felhasználható építkezésre és lövedékként. -item.copper.details = Szokatlanul elterjedt fém a Serpulo-n. Gyenge szerkezetű, de megerősíthető. -item.lead.description = Folyadékszállító csővezetékek építésére használatos, de elektromos eszközökben is alkalmazható. -item.lead.details = Sűrű. Közömbös. Széles körben használatos elemekben.\nMegjegyzés: Valószínűleg mérgező a biológiai életformákra. Nem mintha sok maradt volna errefelé. -item.metaglass.description = Folyadékok szállítására és tárolására alkalmas csővezetékek és épületek építésére, továbbá lövedékként is használható. -item.graphite.description = Elektromos alkatrészek alapanyagaként és lövedékként is használható. -item.sand.description = Egyéb finom nyersanyagok gyártási alapanyaga. -item.coal.description = Tüzelőanyag és gyártási alapanyag. -item.coal.details = Fosszilizálódott növényi anyagnak tűnik, jóval a "spóra incidens" előttről. -item.titanium.description = Folyadékok szállítására, fúrókban és légi járművekben használható. -item.thorium.description = Strapabíró szerkezetekben használható nukleáris tüzelőanyagként. -item.scrap.description = Olvasztással és porítással finom nyersanyagok nyerhetők ki belőle. +item.copper.description = Széleskörűen használatos építkezésnél és lőszerként. +item.copper.details = Réz. Szokatlanul bőséges fém a Serpulón. Megerősítés nélkül strukturálisan gyenge. +item.lead.description = Folyadékszállításnál és elektromos eszközökben használatos. +item.lead.details = Sűrű. Közömbös. Széles körben használatos az akkumulátorokban.\nMegjegyzés: Valószínűleg mérgező a biológiai életformákra. Nem mintha sok maradt volna errefelé. +item.metaglass.description = Folyadékszállító és -tárolóépületeknél használatos. +item.graphite.description = Elektromos alkatrészekben és lőszerként használatos. +item.sand.description = Egyéb finomított nyersanyagok gyártása során használatos. +item.coal.description = Tüzelőanyagként és finomított nyersanyagok gyártásához használatos. +item.coal.details = Fosszilizálódott növényi anyagnak tűnik, jóval a „spóra incidens” előttről. +item.titanium.description = Folyadékszállító épületekben, fúrókban és gyárakban használatos. +item.thorium.description = Strapabíró szerkezetekben használatos nukleáris fűtőanyag. +item.scrap.description = Olvasztókban és porítókban használatos már nyersanyagok finomításához. item.scrap.details = Ősi építmények és egységek hátrahagyott maradványai. -item.silicon.description = Napelemek, összetett áramkörök és nyomkövető lövedékek fontos alapanyaga. Sosincs belőle elég. -item.plastanium.description = Fejlett egységek alapanyagaként, hőszigetelésre és repeszes lövedékekhez is használható. -item.phase-fabric.description = Fejlett elektromos eszközökben és önjavító szerkezetekben is használható. -item.surge-alloy.description = Magas szintű fegyverzetekben és aktív védelemhez is használható. -item.spore-pod.description = Átalakítható Olajjá, vagy robbanószerekké, de használható tüzelőanyagként is. -item.spore-pod.details = Spórák. Egy valószínűleg mesterséges életforma. Más életformák számára halálos gázt bocsátanak ki. Szélsőségesen invazív. Megfelelő körülmények között erősen gyúlékony. -item.blast-compound.description = A bombák és a robbanó lövedékek egyik fő összetevője. -item.pyratite.description = Gyújtó lövedékekben és tüzelőanyag-alapú generátorokban is használható. +item.silicon.description = Napelemekben, összetett áramkörökben és nyomkövető lőszerekben használatos. +item.plastanium.description = Fejlett egységek alapanyagaként, hőszigetelésben és repeszlövedékekben használatos. +item.phase-fabric.description = Fejlett elektromos eszközökben és önjavító épületekben használatos. +item.surge-alloy.description = Fejlett fegyverzetekhez és aktív védelmi épületekhez használatos. +item.spore-pod.description = Olajjá, robbanószerré és tüzelőanyaggá alakításhoz használatos. +item.spore-pod.details = Spórák. Valószínűleg egy mesterséges életforma. Más életformák számára halálos gázt bocsátanak ki. Szélsőségesen invazív. Megfelelő körülmények között erősen gyúlékony. +item.blast-compound.description = Bombákhoz és robbanólövedékekhez használatos. +item.pyratite.description = Gyújtólövedékekben és tüzelőanyag-alapú generátorokban használatos. #Erekir -item.beryllium.description = Sokféle épület- és lőszertípushoz használatos az Erekir-en. -item.tungsten.description = Fúrókban/vágókban, páncélokban használatos, de lőszerként is alkalmazható. A fejlettebb szerkezetek építéséhez szükséges. -item.oxide.description = Hővezetőként és Áramszigetelőként is használatos. -item.carbide.description = Korszerű szerkezetekben, nehezebb egységekben használatos, de lőszerként is alkalmazható. +item.beryllium.description = Sokféle épület- és lőszertípushoz használatos az Erekiren. +item.tungsten.description = Fúrókban, páncélokban és lőszerben használatos. A fejlettebb épületek építéséhez szükséges. +item.oxide.description = Hővezetőként és szigetelőként is használatos az áramtermelésben. +item.carbide.description = Fejlett szerkezetekben, nehezebb egységekhez és lőszerként használatos. -liquid.water.description = Gépek hűtésére és Törmelékfeldolgozásra is használható. -liquid.slag.description = Leválasztóban finomítva értékes fémek forrása, az ellenségre fröcskölve gyilkos fegyver. -liquid.oil.description = Magas szintű nyersanyagok gyártására, vagy gyújtólövedékként is használható. -liquid.cryofluid.description = Hűtőfolyadék az erőművek, reaktorok, lövegtornyok és gyárak számára. +liquid.water.description = Gépek hűtéséhez és törmelékfeldolgozáshoz használatos. +liquid.slag.description = Leválasztóban alkotófémekre finomítható. Folyadékot használó tornyokban lőszerként használható. +liquid.oil.description = Fejlett nyersanyagok gyártásához és gyújtólövedékhez használatos. +liquid.cryofluid.description = Reaktorokban, lövegtornyokban és gyárakban használatos hűtőfolyadékként. #Erekir liquid.arkycite.description = Kémiai reakciókban használatos energiatermelésre és anyagszintézisre. -liquid.ozone.description = Oxidálószerként használatos az anyaggyártásban és üzemanyagként. Mérsékelten robbanékony. -liquid.hydrogen.description = A nyersanyagok kitermelésében, egységgyártásban és szerkezetjavításban is használatos. Gyúlékony. -liquid.cyanogen.description = Lőszerként, vagy fejlett egységek építéséhez és különböző reakciókhoz használatos a fejlett épületekben. Erősen gyúlékony. -liquid.nitrogen.description = A nyersanyagok kitermelésében, gáztermelésnél és egységgyártásnál is használatos. Semleges gáz. -liquid.neoplasm.description = A Neoplázia Reaktor veszélyes biológiai mellékterméke. Gyorsan átterjed minden szomszédos víztartalmú épületre, amelyhez hozzáér és közben károsítja azokat. Sűrű folyadék. -liquid.neoplasm.details = Neoplazma. Egy kontrollálhatatlan, gyorsan osztódó, iszap állagú, szintetikus sejtmassza. Hőálló. Rendkívül veszélyes minden Vízzel kapcsolatos szerkezetre.\n\nTúl összetett és instabil a szabványos elemzésekhez. Potenciális alkalmazási területe ismeretlen. Ajánlott a Salak medencékben való elégetése. +liquid.ozone.description = Az anyaggyártásban oxidálószerként, illetve üzemanyagként használatos. Mérsékelten robbanékony. +liquid.hydrogen.description = A nyersanyagok kitermelésében, egységgyártásban és szerkezetjavításban használatos. Gyúlékony. +liquid.cyanogen.description = Lőszerként, fejlett egységek építéséhez és különböző reakciókhoz használatos a fejlett blokkokban. Erősen gyúlékony. +liquid.nitrogen.description = A nyersanyagok kitermelésénél, gáztermelésnél és egységgyártásnál is használatos. Semleges gáz. +liquid.neoplasm.description = A neoplázia reaktor veszélyes biológiai mellékterméke. Gyorsan átterjed minden szomszédos víztartalmú blokkra, amelyhez hozzáér, és közben károsítja azokat. Sűrű folyadék. +liquid.neoplasm.details = Neoplazma. Egy kontrollálhatatlan, gyorsan osztódó, iszap állagú, szintetikus sejtmassza. Hőálló. Rendkívül veszélyes minden vízzel kapcsolatos szerkezetre.\n\nTúl összetett és instabil a szabványos elemzésekhez. Potenciális alkalmazási területe ismeretlen. A salakmedencékben való elégetés ajánlott. -block.derelict = \uf77e [lightgray]Elhagyatott -block.armored-conveyor.description = Nyersanyagokat szállít. Nem fogad el nyersanyagot oldalról. +block.derelict =  [lightgray]Elhagyatott +block.armored-conveyor.description = Nyersanyagokat szállít. Nem fogad el oldalról nem szállítószalagról érkező nyersanyagot. block.illuminator.description = Világít. -block.message.description = Üzenetet tárol szövetségesek kommunikációjához. +block.message.description = Üzenetet tárol a szövetségesek kommunikációjához. block.reinforced-message.description = Üzenetet tárol a szövetségesek közötti kommunikációhoz. block.world-message.description = A pályakészítésben használható üzenetblokk. Nem lehet megsemmisíteni. -block.graphite-press.description = Szenet présel Grafittá. -block.multi-press.description = Szenet sajtol Grafittá. Hatékonyan dolgozik, de Vizet igényel hűtéshez. -block.silicon-smelter.description = Szilíciumot nyer ki Homok és Szén keverékéből. -block.kiln.description = Ólomból és Homokból olvaszt Ólomüveget. -block.plastanium-compressor.description = Műanyagot gyárt Olaj és Titán felhasználásával. -block.phase-weaver.description = Tóritkvarcot képez Tórium és Homok keverékéből. -block.surge-smelter.description = Titán, Ólom, Szilícium és Réz olvadékából állít elő Elektrometált. -block.cryofluid-mixer.description = Finom Titánport kever Vízhez a Hűtőfolyadék előállításához. -block.blast-mixer.description = Piratitból és Spóra Kapszulákból készít Robbanóelegyet. -block.pyratite-mixer.description = Szenet, Homokot és Ólmot vegyít Piratittá. -block.melter.description = Törmeléket olvaszt Salakká. -block.separator.description = Szétbontja a Salakot ásványi összetevőire. -block.spore-press.description = Nagy nyomáson Olajat sajtol a Spóra Kapszulából. -block.pulverizer.description = Finom Homokká őrli a Törmeléket. -block.coal-centrifuge.description = Szenet nyer ki Olajból. +block.graphite-press.description = Grafittá préseli a szenet. +block.multi-press.description = Grafittá préseli a szenet. Hűtése vizet igényel. +block.silicon-smelter.description = A homokot és szenet szilíciummá finomítja. +block.kiln.description = Ólomüveget olvaszt az ólomból és a homokból. +block.plastanium-compressor.description = Olaj és titán felhasználásával műanyagot gyárt. +block.phase-weaver.description = Tórium és homok keverékéből tóritkvarcot állít elő. +block.surge-smelter.description = Titán, ólom, szilícium és réz ötvözésével elektrometált állít elő. +block.cryofluid-mixer.description = Finom titánpor vízhez keverésével hűtőfolyadékot állít elő. +block.blast-mixer.description = Robbanóelegyet készít a piratitból és a spórakapszulákból. +block.pyratite-mixer.description = Piratittá vegyíti a szenet, homokot és ólmot. +block.melter.description = Salakká olvasztja a törmeléket. +block.separator.description = Ásványi összetevőire bontja a salakot. +block.spore-press.description = Olajat sajtol a spórakapszulából. +block.pulverizer.description = Finom homokká őrli a törmeléket. +block.coal-centrifuge.description = Szénné alakítja az olajat. block.incinerator.description = Megsemmisít minden nyersanyagot és folyadékot. -block.power-void.description = Elnyel minden Áramot. (Csak homokozóban.) -block.power-source.description = Végtelen Áramot termel. (Csak homokozóban.) -block.item-source.description = Végtelen nyersanyagot bocsát ki. (Csak homokozóban.) -block.item-void.description = Megsemmisít minden nyersanyagot. (Csak homokozóban.) -block.liquid-source.description = Végtelen folyadékot bocsát ki. (Csak homokozóban.) -block.liquid-void.description = Megsemmisít minden folyadékot. (Csak homokozóban.) -block.payload-source.description = Végtelenül sok rakomány. (Csak homokozóban.) -block.payload-void.description = Minden rakomány megsemmisítése. (Csak homokozóban.) +block.power-void.description = Elnyel minden áramot. Csak homokozó módban. +block.power-source.description = Végtelen áramot termel. Csak homokozó módban. +block.item-source.description = Végtelen nyersanyagot termel. Csak homokozó módban. +block.item-void.description = Megsemmisít minden nyersanyagot. Csak homokozó módban. +block.liquid-source.description = Végtelen folyadékot termel. Csak homokozó módban. +block.liquid-void.description = Megsemmisít minden folyadékot. Csak homokozó módban. +block.payload-source.description = Végtelen rakományt termel. Csak homokozó módban. +block.payload-void.description = Megsemmisít minden rakományt. Csak homokozó módban. block.copper-wall.description = Megvédi az épületeket az ellenséges lövedékektől. block.copper-wall-large.description = Megvédi az épületeket az ellenséges lövedékektől. block.titanium-wall.description = Megvédi az épületeket az ellenséges lövedékektől. @@ -2027,155 +2027,155 @@ block.phase-wall.description = Megvédi az épületeket az ellenséges lövedék block.phase-wall-large.description = Megvédi az épületeket az ellenséges lövedékektől, a legtöbb lövedék visszapattan róla. block.surge-wall.description = Megvédi az épületeket az ellenséges lövedékektől, periodikusan elektromos kisüléseket generál, ha hozzáérnek. block.surge-wall-large.description = Megvédi az épületeket az ellenséges lövedékektől, periodikusan elektromos kisüléseket generál, ha hozzáérnek. -block.door.description = Fal, amit nyitni és zárni lehet. -block.door-large.description = Fal, amit nyitni és zárni lehet. De ez nagyobb. -block.mender.description = Javítja az épületeket a hatókörén belül.\nSzilíciummal növelhető a hatósugara és hatékonysága. -block.mend-projector.description = Javítja az épületeket a hatókörén belül.\nTóritkvarccal növelhető a hatósugara és hatékonysága. +block.door.description = Nyitható és zárható fal. +block.door-large.description = Nyitható és zárható fal. +block.mender.description = Időnként javítja a közeli épületeket.\nSzilíciummal növelhető a hatósugara és hatékonysága. +block.mend-projector.description = Javítja a közeli épületeket.\nTóritkvarccal növelhető a hatósugara és hatékonysága. block.overdrive-projector.description = Növeli a közeli épületek termelési sebességét.\nTóritkvarccal növelhető a hatósugara és hatékonysága. -block.force-projector.description = Hatszögletű erőteret hoz létre maga körül, amely megvédi az épületeket és a benne lévő egységeket a sérüléstől.\nTúlmelegszik, ha túl sok sérülést szenved. Opcionálisan Hűtőfolyadékot használ a túlmelegedés megakadályozására. A Tóritkvarc növeli a pajzs méretét. +block.force-projector.description = Hatszögletű erőteret hoz létre maga körül, amely megvédi az épületeket és a benne lévő egységeket a sérüléstől.\nTúlmelegszik, ha túl sok sérülést szenved. Hűtőfolyadékot használva megakadályozható a túlmelegedés. A tóritkvarc növeli a pajzs méretét. block.shock-mine.description = Elektromos kisülést hoz létre, ha ellenséggel érintkezik. -block.conveyor.description = Szállítószalag. Nyersanyagokat szállít. -block.titanium-conveyor.description = Nyersanyagokat szállít. Gyorsabb a sima Szállítószalagnál. -block.plastanium-conveyor.description = Nyersanyagokat szállít tömbösítve. Hátulról fogadja a nyersanyagokat, elöl három irányba szétosztja őket. Több kezdő- és végponttal növelhető az áteresztőképessége. -block.junction.description = Hídként működik két egymást keresztező Szállítószalag között. +block.conveyor.description = Nyersanyagokat szállít. +block.titanium-conveyor.description = Nyersanyagokat szállít. Gyorsabb a sima szállítószalagnál. +block.plastanium-conveyor.description = Nyersanyagokat szállít tömbösítve. Hátulról fogadja a nyersanyagokat, elöl három irányba osztja szét őket. Több kezdő- és végponttal növelhető az áteresztőképessége. +block.junction.description = Hídként működik két egymást keresztező szállítószalag között. block.bridge-conveyor.description = Nyersanyagokat szállít épületek és terepakadályok fölött. -block.phase-conveyor.description = Nyersanyagokat szállít épületek és terepakadályok fölött. Nagyobb távolságra ér, mint a sima Szállítószalag Híd, de Áramot használ. +block.phase-conveyor.description = Nyersanyagokat szállít épületek és terepakadályok fölött. Nagyobb távolságra ér, mint a sima szállítószalaghíd, de áramot igényel. block.sorter.description = Csak a kiválasztott nyersanyagot engedi tovább egyenesen, minden mást oldalra ad ki. -block.inverted-sorter.description = A kiválasztott nyersanyagot oldalra adja ki, minden mást egyenesen enged tovább. -block.router.description = Háromfelé osztja szét a beérkező nyersanyagokat. -block.router.details = Pokoli masina. Ne tedd közvetlenül gyárak mellé, mert az épületek nyersanyagai eltömítik. -block.distributor.description = Hétfelé osztja szét a beérkező nyersanyagokat. -block.overflow-gate.description = Csak akkor ad ki nyersanyagot oldalra, ha előrefelé már nem tud. Nem használható közvetlenül Túlfolyó Kapu, vagy Alul-folyó Kapu mellett. -block.underflow-gate.description = Csak akkor enged tovább nyersanyagokat előre, ha oldalra már nem tudja kiadni őket. Nem használható közvetlenül Túlfolyó Kapu, vagy Alul-folyó Kapu mellett. -block.mass-driver.description = Nagy hatótávolságú nyersanyagszállító eszköz. Rakományokban lő át nyersanyagokat egy másik, hozzákapcsolt Tömegmozgatónak. -block.mechanical-pump.description = Folyadékot szivattyúz. Nem igényel Áramot. -block.rotary-pump.description = Folyadékot szivattyúz. Árammal működik. -block.impulse-pump.description = Folyadékot szivattyúz. Sokat termel, de sok Áramot fogyaszt. -block.conduit.description = Folyadékot szállít. -block.pulse-conduit.description = Folyadékot szállít. Gyorsabb és nagyobb tárolókapacitású, mint a sima Csővezeték. -block.plated-conduit.description = Folyadékot szállít. Nem fogad el folyadékot oldalról. Nem önti ki a folyadékot, ha nincs a végén semmi. -block.liquid-router.description = Háromfelé osztja szét a beérkező folyadékot. Bizonyos mennyiség tárolására is képes. -block.liquid-container.description = Jelentős mennyiségű folyadékot tárol. Minden oldalon kijuttatja a folyadékot, hasonlóan a Folyadék Elosztóhoz. -block.liquid-tank.description = Nagy mennyiségű folyadékot tárol és minden oldalán képes azt kijuttatni. +block.inverted-sorter.description = Hasonló a szokásos válogatóhoz, de a kiválasztott nyersanyagot oldalra adja ki. +block.router.description = Egyenletesen háromfelé osztja szét a beérkező nyersanyagokat. +block.router.details = Egy szükséges rossz. Nem ajánlott termelőegységek mellett használni, mert a kimenet eltömíti. +block.distributor.description = Egyenletesen hétfelé osztja szét a beérkező nyersanyagokat. +block.overflow-gate.description = Csak akkor ad ki nyersanyagot oldalra, ha előrefelé már nem tud. +block.underflow-gate.description = A túlcsorduló kapu ellentettje. Csak akkor ad ki nyersanyagot előrefelé, ha oldalra már nem tud. +block.mass-driver.description = Nagy hatótávolságú nyersanyagszállító eszköz. Rakományokat gyűjt össze, és átlövi egy másik, hozzákapcsolt tömegmozgatónak. +block.mechanical-pump.description = Folyadékot szivattyúz és ad ki. Nem igényel áramot. +block.rotary-pump.description = Folyadékot szivattyúz és ad ki. Áramot igényel. +block.impulse-pump.description = Folyadékot szivattyúz és ad ki. +block.conduit.description = Folyadékot szállít. Pumpákkal és egyéb csővezetékekkel együtt használatos. +block.pulse-conduit.description = Folyadékot szállít. Gyorsabban szállít, és nagyobb tárolókapacitású, mint a szokásos csővezeték. +block.plated-conduit.description = Folyadékot szállít. Nem fogad el folyadékot oldalról. Nem szivárog, ha nincs a végén semmi. +block.liquid-router.description = Egyenletesen háromfelé osztja szét a beérkező folyadékot. Bizonyos mennyiség tárolására is képes. +block.liquid-container.description = Jelentős mennyiségű folyadékot tárol. Minden oldalon kiadja, hasonlóan a folyadékelosztóhoz. +block.liquid-tank.description = Nagy mennyiségű folyadékot tárol. Minden oldalon kiadja, hasonlóan a folyadékelosztóhoz. block.liquid-junction.description = Hídként működik két egymást keresztező csővezeték között. block.bridge-conduit.description = Folyadékot szállít épületek és terepakadályok fölött. -block.phase-conduit.description = Folyadékot szállít épületek és terepakadályok fölött. Nagyobb távolságra ér, mint a sima Csővezeték Híd, de Áramot használ. -block.power-node.description = Áramot vezet az összekapcsolt épületekhez. Az érintkező épületekkel automatikusan kapcsolatban van. -block.power-node-large.description = Nagyobb Villanyoszlop nagyobb hatótávolsággal. -block.surge-tower.description = Hosszútávú Villanyoszlop, csak kevés kapcsolatra képes. -block.diode.description = Az eltárolt Áramot irányítja át egy megadott irányba, de csak akkor, ha a fogadó oldalon van kevesebb tárolva. -block.battery.description = Áramot tárol el, ha túltermelés van. Leadja az Áramot, ha hiány van. -block.battery-large.description = Áramot tárol el, ha túltermelés van. Leadja az Áramot, ha hiány van. Nagyobb kapacitású a sima Akkumulátornál. -block.combustion-generator.description = Áramot termel éghető anyagok elégetésével. -block.thermal-generator.description = Forró környezetben Áramot termel. -block.steam-generator.description = Áramot termel éghető anyagok elégetésével és Víz gőzzé alakításával. -block.differential-generator.description = Egy lórúgásnyi Áramot termel. Hasznosítja a Hűtőfolyadék és az égő Piratit hőmérséklet különbségét. -block.rtg-generator.description = A radioaktív bomlás energiáját hasznosítja, hogy lassan de biztosan Áramot termeljen. -block.solar-panel.description = Napfényből állít elő kevés Áramot. -block.solar-panel-large.description = Napfényből állít elő kevés Áramot. Hatékonyabb a sima Napelemnél. -block.thorium-reactor.description = Jelentős Áramot állít elő Tóriumból. Állandó hűtést igényel. Ha túlmelegszik, akkor felrobban. -block.impact-reactor.description = Csúcsra járatva rengeteg Áramot termel. Jelentős Árambefektetést igényel a reakció beindítása. -block.mechanical-drill.description = Ércre helyezve kis tempóban termeli ki az adott nyersanyagot. Csak alapvető nyersanyagok kitermelésére képes. -block.pneumatic-drill.description = Egy fejlettebb fúró, képes Titán kitermelésére. Gyorsabban dolgozik a Mechanikus Fúrónál. -block.laser-drill.description = Lézerek használatával még gyorsabban tud dolgozni, de Áramot használ. Képes Tóriumot kitermelni. -block.blast-drill.description = A technológia csúcsa. Rengeteg Áramot használ. -block.water-extractor.description = Képes a talajvíz kiszivattyúzására. Akkor használd, ha nincs elérhető Víz a felszínen. +block.phase-conduit.description = Folyadékot szállít épületek és terepakadályok fölött. Nagyobb távolságra ér, mint a sima csővezetékhíd, de áramot igényel. +block.power-node.description = Áramot vezet az összekapcsolt csomópontokhoz. A szomszédos blokkokkal automatikusan kapcsolatban van. +block.power-node-large.description = Nagyobb villanyoszlop, nagyobb hatótávolsággal. +block.surge-tower.description = Hosszútávú villanyoszlop, kevesebb elérhető kapcsolattal. +block.diode.description = Az eltárolt áramot irányítja egy irányba továbbítja, de csak akkor, ha a fogadó oldalon kevesebb van tárolva. +block.battery.description = Áramot tárol el, ha túltermelés van. Leadja az áramot, ha hiány van. +block.battery-large.description = Áramot tárol el, ha túltermelés van. Leadja az áramot, ha hiány van. Nagyobb kapacitású a szokásos akkumulátornál. +block.combustion-generator.description = Áramot termel éghető anyagok, például szén, elégetésével. +block.thermal-generator.description = Forró környezetben áramot termel. +block.steam-generator.description = Éghető anyagok elégetésével és víz gőzzé alakításával áramot termel. +block.differential-generator.description = Nagy mennyiségű áramot termel. A hűtőfolyadék és az égő piratit hőmérséklet-különbségét használja ki. +block.rtg-generator.description = A radioaktív bomlás energiáját hasznosítja, hogy lassan, de biztosan áramot termeljen. +block.solar-panel.description = Napfényből állít elő kevés áramot. +block.solar-panel-large.description = Napfényből állít elő kevés áramot. Hatékonyabb a szokásos napelemnél. +block.thorium-reactor.description = Jelentős mennyiségű áramot állít elő tóriumból. Állandó hűtést igényel. Ha nincs megfelelően hűtőfolyadékkal ellátva, akkor felrobban. +block.impact-reactor.description = Csúcsra járatva rengeteg áramot termel. A reakció beindítása jelentős árambefektetést igényel. +block.mechanical-drill.description = Ércre helyezve lassú tempóban termeli ki az adott nyersanyagot. Csak alapvető nyersanyagok kitermelésére képes. +block.pneumatic-drill.description = Egy fejlettebb fúró, mely titán kitermelésére is képes. Gyorsabban dolgozik a mechanikus fúrónál. +block.laser-drill.description = Lézerek használatával még gyorsabban tud dolgozni, de áramot igényel. Képes tóriumot kitermelni. +block.blast-drill.description = A technológia csúcsa. Nagy mennyiségű áramot igényel. +block.water-extractor.description = Kiszivattyúzza a talajvizet. Olyan helyeken használatos, ahol nem érhető el felszíni vízforrás. block.cultivator.description = A légkörben szálló spórákat kapszulákba sűríti. -block.cultivator.details = Visszaszerzett technológia. Hatalmas tömegű biomassza gyártására alkalmas a lehető leghatékonyabban. Valószínűleg a Serpulo-t ma borító spórák kezdeti inkubátora. -block.oil-extractor.description = Nagy mennyiségben használ Vizet, Homokot és Áramot, hogy Olajat nyerjen ki a földből. +block.cultivator.details = Visszaszerzett technológia. Hatalmas tömegű biomassza gyártására alkalmas a lehető leghatékonyabban. Valószínűleg a Serpulo felszínét ma borító spórák kezdeti inkubátora. +block.oil-extractor.description = Nagy mennyiségű áramot, homokot és vizet használ, hogy olajat fúrjon. block.core-shard.description = Támaszpont. Ha elpusztul, a szektor elveszett. -block.core-shard.details = Az első modell. Kompakt. Önsokszorosító. Egyszer használatos gyorsítófúvókákkal van felszerelve, nem bolygóközi utazásra tervezték. -block.core-foundation.description = Támaszpont. Páncélozott. Több nyersanyagot tárol, mint a Szilánk. +block.core-shard.details = Az első modell. Kompakt. Önsokszorosító. Egyszer használatos indítórakétákkal van felszerelve, nem bolygóközi utazásra tervezték. +block.core-foundation.description = Támaszpont. Jól páncélozott. Több nyersanyagot tárol, mint a Szilánk. block.core-foundation.details = A második modell. -block.core-nucleus.description = Támaszpont. Megerősített páncélzattal. Hatalmas mennyiségű nyersanyag tárolására képes. +block.core-nucleus.description = Támaszpont. Rendkívül jól páncélozott. Hatalmas mennyiségű nyersanyag tárolására képes. block.core-nucleus.details = A harmadik, végső modell. -block.vault.description = Nagy mennyiséget tárol minden nyersanyagtípusból. A tartalma kirakodó segítségével nyerhető ki. -block.container.description = Kis mennyiséget tárol minden nyersanyagtípusból. A tartalma kirakodó segítségével nyerhető ki. +block.vault.description = Nagy mennyiséget tárol minden nyersanyagtípusból. Bővíti a raktárat, ha egy támaszpont mellé van helyezve. A tartalma kirakodó segítségével nyerhető ki. +block.container.description = Kis mennyiséget tárol minden nyersanyagtípusból. Bővíti a raktárat, ha egy támaszpont mellé van helyezve. A tartalma kirakodó segítségével nyerhető ki. block.unloader.description = Kirakodja a szomszédos épületekből a kiválasztott nyersanyagot. block.launch-pad.description = Nyersanyagokat juttat el más szektorokba. -block.launch-pad.details = Szub-orbitális rendszer a nyersanyagok szektorok között történő szállítására. A teherkapszulák törékenyek, ezért nem képesek túlélni a visszatérést. +block.launch-pad.details = Szuborbitális rendszer a nyersanyagok szektorok között történő szállítására. A teherkapszulák törékenyek, ezért nem képesek túlélni a légkörbe való visszatérést. block.duo.description = Változatos lövedékekkel lő az ellenségre. -block.scatter.description = Ólom-, Ólomüveg-, vagy Törmelékdarabokkal tüzel az ellenséges légierőre. +block.scatter.description = Ólom-, törmelék- vagy ólomüvegdarabokat lő az ellenséges légijárművekre. block.scorch.description = Megégeti az ellenség közeli földi egységeit. Kis távolságra nagyon hatékony. -block.hail.description = Kis lövedékeket lő ki a nagy távolságokra lévő földi célpontokra. -block.wave.description = Folyadékot önt az ellenségre. Eloltja a tüzet, ha Vízzel van feltöltve. +block.hail.description = Kis lövedékeket lő ki nagy távolságokra lévő földi célpontokra. +block.wave.description = Folyadékot önt az ellenségre. Eloltja a tüzeket, ha vízzel van ellátva. block.lancer.description = Erős energiasugarakat lő közeli földi célpontokra. block.arc.description = Elektromos szikrákat kelt földi célpontok között. block.swarmer.description = Nyomkövető rakétákat lő az ellenségre. -block.salvo.description = Kis sorozatokat lő az ellenségre. -block.fuse.description = Három kis hatótávú átütő erejű töltényt lő egyszerre. -block.ripple.description = Lövedékek csoportjával tüzel földi célpontokra nagy távolságra. +block.salvo.description = Gyors sorozatokat lő az ellenségre. +block.fuse.description = Három kis hatótávú, átütő erejű lövedéket lő a közeli ellenségre. +block.ripple.description = Lövedékek csoportjával tüzel nagy távolságra lévő földi célpontokra. block.cyclone.description = Robbanó lövedékeket lő közeli ellenségekre. -block.spectre.description = Nagy, a páncélon is áthatoló lövedékekkel tüzel légi és földi célpontokra is. -block.meltdown.description = Feltöltődés után folyamatos lézersugarat lő a közeli ellenségekre. Hűtést igényel. -block.foreshadow.description = Nagy méretű villámot lő ki nagy távolságokra, de egyszerre csak egyetlen célpontra. -block.repair-point.description = Folyamatosan gyógyítja a legközelebbi sérült egységet a közelében. +block.spectre.description = Nagy lövedékekkel tüzel légi és földi célpontokra. +block.meltdown.description = Feltöltődés után folyamatos lézersugarat lő a közeli ellenségekre. A működéséhez hűtőfolyadékot igényel. +block.foreshadow.description = Egy nagy villámot lő ki egy nagy távolságra lévő célpontra. A magasabb maximális életerővel rendelkező ellenségeket részesíti előnyben. +block.repair-point.description = Folyamatosan javítja a legközelebbi sérült egységet a közelében. block.segment.description = Megsemmisíti a beérkező lövedékeket. A lézerrel szemben hatástalan. -block.parallax.description = Vonónyalábot bocsát ki, amivel magához vonzza és sebzi a repülő egységeket. -block.tsunami.description = Erős folyadékhullámokat lő az ellenségre. Eloltja a tüzet, ha Vízzel van feltöltve. -block.silicon-crucible.description = Szilíciumot finomít Homokból és Szénből. Piratitot használ kiegészítő hőforrásként. Forró környezetben hatékonyabb. -block.disassembler.description = Ritka ásványi elemeket választ ki Salakból. Képes Tóriumot kiválasztani. -block.overdrive-dome.description = Megnöveli a környező gyárak termelési sebességét. Tóritkvarcot és Szilíciumot igényel. -block.payload-conveyor.description = Képes egységeket szállítani. -block.payload-router.description = Háromfelé osztja szét a beérkező egységeket. -block.ground-factory.description = Földi egységeket gyárt. A kész egységek azonnal hadra-foghatóak, vagy Újratervezőkben tovább fejleszthetőek. -block.air-factory.description = Légi egységeket gyárt. A kész egységek azonnal hadra-foghatóak, vagy Újratervezőkben tovább fejleszthetőek. -block.naval-factory.description = Vízi egységeket gyárt. A kész egységek azonnal hadra-foghatóak, vagy Újratervezőkben tovább fejleszthetőek. +block.parallax.description = Vonónyalábot bocsát ki, amivel magához vonzza, és közben sebzi a légi egységeket. +block.tsunami.description = Erős folyadékhullámot lő az ellenségre. Eloltja a tüzeket, ha vízzel van ellátva. +block.silicon-crucible.description = Szilíciumot finomít homokból és szénből, piratitot használ kiegészítő hőforrásként. Forró környezetben még hatékonyabb. +block.disassembler.description = Ritka ásványi összetevőket válogat ki a salakból, alacsony hatékonysággal. Képes tóriumot kiválogatni. +block.overdrive-dome.description = Megnöveli a környező épületek termelési sebességét. A működtetése tóritkvarcot és szilíciumot igényel. +block.payload-conveyor.description = Nagy mennyiségű terhet mozgatni, például gyárakból érkező nyersanyagokat. Mágneses. Használható súlytalanságban. +block.payload-router.description = Háromfelé osztja szét a beérkező terhet. Rendezőként is szolgál, ha van megadva szűrő. Mágneses. Használható súlytalanságban. +block.ground-factory.description = Földi egységeket gyárt. A kész egységek azonnal hadra foghatók, vagy újratervezőkben továbbfejleszthetők. +block.air-factory.description = Légi egységeket gyárt. A kész egységek azonnal hadra foghatók, vagy újratervezőkben továbbfejleszthetők. +block.naval-factory.description = Vízi egységeket gyárt. A kész egységek azonnal hadra foghatók, vagy újratervezőkben továbbfejleszthetők. block.additive-reconstructor.description = Kettes szintre fejleszti a beérkező egységeket. block.multiplicative-reconstructor.description = Hármas szintre fejleszti a beérkező egységeket. block.exponential-reconstructor.description = Négyes szintre fejleszti a beérkező egységeket. -block.tetrative-reconstructor.description = Ötös szintre fejleszti a beérkező egységeket. -block.switch.description = Kétállású kapcsoló. Az állapota leolvasható és módosítható processzorokkal. -block.micro-processor.description = Logikai műveletek sorozatát hajtja végre végtelenítve. Használható egységek, vagy épületek irányítására is. -block.logic-processor.description = Logikai műveletek sorozatát hajtja végre végtelenítve. Használható egységek, vagy épületek irányítására is. Gyorsabb mint a Mikroprocesszor. -block.hyper-processor.description = Logikai műveletek sorozatát hajtja végre végtelenítve. Használható egységek, vagy épületek irányítására is. Gyorsabb mint a Logikai Processzor. -block.memory-cell.description = Információt tárol processzorok számára. -block.memory-bank.description = Információt tárol processzorok számára. Nagyobb kapacitású. -block.logic-display.description = Ábrák rajzolhatók rá processzorral. -block.large-logic-display.description = Ábrák rajzolhatók rá processzorral. -block.interplanetary-accelerator.description = Hatalmas elektromágneses gyorsítótorony. Képes Támaszpontokat szökési sebességre gyorsítani bolygóközi bevetéshez. -block.repair-turret.description = Folyamatosan javítja a közelében lévő legközelebbi sérült egységet. Opcionálisan elfogadja a Hűtőfolyadékot. +block.tetrative-reconstructor.description = Ötös szintre (a végsőre) fejleszti a beérkező egységeket. +block.switch.description = Kétállású kapcsoló. Az állapota logikai processzorokkal olvasható és vezérelhető. +block.micro-processor.description = Logikai műveletek sorozatát hajtja végre egy ciklusban. Egységek vagy épületek irányítására használható. +block.logic-processor.description = Logikai műveletek sorozatát hajtja végre egy ciklusban. Egységek vagy épületek irányítására használható. Gyorsabb mint a mikroprocesszor. +block.hyper-processor.description = Logikai műveletek sorozatát hajtja végre egy ciklusban. Egységek vagy épületek irányítására használható. Gyorsabb mint a logikai processzor. +block.memory-cell.description = Információt tárol egy logikai processzor számára. +block.memory-bank.description = Információt tárol egy logikai processzor számára. Nagy kapacitású. +block.logic-display.description = Tetszőleges ábrákat jelenít meg egy logikai processzor alapján. +block.large-logic-display.description = Tetszőleges ábrákat jelenít meg egy logikai processzor alapján. +block.interplanetary-accelerator.description = Hatalmas elektromágneses sínágyútorony. Képes a támaszpontokat szökési sebességre gyorsítani a bolygóközi bevetéshez. +block.repair-turret.description = Folyamatosan javítja a közelében lévő legközelebbi sérült egységet. Opcionálisan elfogad hűtőfolyadékot. #Erekir block.core-bastion.description = Támaszpont. Páncélozott. Ha elpusztul, a szektor elveszett. block.core-citadel.description = Támaszpont. Nagyon jól páncélozott. Több nyersanyagot tárol, mint a Bástya. block.core-acropolis.description = Támaszpont. Kivételesen jól páncélozott. Több nyersanyagot tárol, mint a Citadella. -block.breach.description = Átütő erejű Berillium-, vagy Volfrámlövedékeket lő ki az ellenséges célpontokra. -block.diffuse.description = Széles kúpban lövi ki a lövedékeket. Visszalöki az ellenséges célpontokat. -block.sublimate.description = Folyamatos lángcsóvát lő ki az ellenséges célpontokra. Átüti a páncélt. -block.titan.description = Hatalmas robbanó ágyúlövedéket lő ki földi célpontokra. Hidrogént igényel. -block.afflict.description = Hatalmas töltésű repeszlövedék gömböket lő ki. Fűtést igényel. -block.disperse.description = Égő repeszlövedékeket lő ki légi célpontokra. -block.lustre.description = Lassan mozgó, egyszerre egy célpontra ható lézert lő ki az ellenséges célpontokra. +block.breach.description = Átütő erejű berillium- vagy volfrámlövedéket lő az ellenséges célpontokra. +block.diffuse.description = Széles kúpban lő ki lövedékeket. Visszalöki az ellenséges célpontokat. +block.sublimate.description = Folyamatos lángcsóvát lő az ellenséges célpontokra. Átüti a páncélt. +block.titan.description = Hatalmas robbanóanyagú tüzérségi lövedéket lő földi célpontokra. Hidrogént igényel. +block.afflict.description = Hatalmas töltésű repeszlövedék-gömböket lő ki. Hőt igényel. +block.disperse.description = Égő repeszlövedékeket lő légi célpontokra. +block.lustre.description = Lassan mozgó, egyszerre egy célpontra ható lézert lő az ellenséges célpontokra. block.scathe.description = Nagy erejű rakétát indít jelentős távolságokra lévő földi célpontok ellen. block.smite.description = Átütő erejű, villámló lövedékeket lő ki. -block.malign.description = Lézer töltetekből álló célzott sortüzet zúdít az ellenséges célpontokra. Nagy mennyiségű Hőt igényel. -block.silicon-arc-furnace.description = Szilíciumot finomít Homokból és Grafitból. -block.oxidation-chamber.description = A Berilliumot és az Ózont Oxiddá alakítja. Melléktermékként Hőt bocsát ki. -block.electric-heater.description = Fűti a vele szemben álló épületeket. Nagy mennyiségű Áramot igényel. +block.malign.description = Lézertöltetekből álló célzott sortüzet zúdít az ellenséges célpontokra. Jelentős fűtést igényel. +block.silicon-arc-furnace.description = A homokot és grafitot szilíciummá finomítja. +block.oxidation-chamber.description = A berilliumot és az ózont oxiddá alakítja. Melléktermékként hőt bocsát ki. +block.electric-heater.description = Fűti a vele szemben álló épületeket. Nagy mennyiségű áramot igényel. block.slag-heater.description = Fűti a vele szemben álló épületeket. Salakot igényel. block.phase-heater.description = Fűti a vele szemben álló épületeket. Tóritkvarcot igényel. -block.heat-redirector.description = Átirányítja a felgyülemlett Hőt más épületekbe. -block.heat-router.description = A felgyülemlett Hőt három kimeneti irányba terjeszti. -block.electrolyzer.description = A Vizet Hidrogén- és Ózongázzá alakítja. -block.atmospheric-concentrator.description = Kondenzálja a Nitrogént a légkörből. Hőt igényel. -block.surge-crucible.description = A Salakból és a Szilíciumból Elektrometált olvaszt. Hőt igényel. -block.phase-synthesizer.description = Tóriumból, Homokból és Ózonból Tóritkvarcot szintetizál. Hőt igényel. -block.carbide-crucible.description = A Grafitot és a Volfrámot Karbiddá olvasztja. Hőt igényel. -block.cyanogen-synthesizer.description = Cianogént szintetizál Arkycitből és Grafitból. Hőt igényel. -block.slag-incinerator.description = Elégeti a nem illékony tárgyakat, vagy folyadékokat. Salakot igényel. -block.vent-condenser.description = A kürtőkből kiáramló gázokat Vízzé kondenzálja. Áramot igényel. -block.plasma-bore.description = Ércfallal szemben elhelyezve, korlátlanul termeli ki a nyersanyagokat. Kis mennyiségű Áramot igényel. -block.large-plasma-bore.description = Egy nagyobb Plazma Vágó. Képes a Volfrám és a Tórium bányászatára. Hidrogént és Áramot igényel. -block.cliff-crusher.description = Felőrli a Homokfalakat és korlátlan ideig Homokot termel. Áramot igényel. A hatékonysága a fal típusától függően változik. -block.impact-drill.description = Ha ércre helyezik, korlátlan ideig, sorozatokban termeli ki a nyersanyagokat. Áramot és Vizet igényel. -block.eruption-drill.description = Továbbfejlesztett Ütvefúró. Képes Tóriumot bányászni. Hidrogént igényel. -block.reinforced-conduit.description = Előre szállítja a folyadékokat. Nem fogadja el a nem csővezetékes bemeneteket oldalról. +block.heat-redirector.description = Más blokkokba irányítja a felgyülemlett hőt. +block.heat-router.description = A felgyülemlett hőt három kimeneti irányba osztja. +block.electrolyzer.description = A vizet hidrogénné és ózonná alakítja. A keletkező gázokat két ellentétes irányba adja ki, melyeket a megfelelő színek jelölik. +block.atmospheric-concentrator.description = Koncentrálja a légkörben lévő nitrogént. Hőt igényel. +block.surge-crucible.description = Salakból és szilíciumból elektrometált olvaszt. Hőt igényel. +block.phase-synthesizer.description = Tóriumból, homokból és ózonból tóritkvarcot szintetizál. Hőt igényel. +block.carbide-crucible.description = A grafitot és a volfrámot karbiddá olvasztja. Hőt igényel. +block.cyanogen-synthesizer.description = Arkicitből és grafitból diciánt szintetizál. Hőt igényel. +block.slag-incinerator.description = Elégeti a nem illékony nyersanyagokat vagy folyadékokat. Salakot igényel. +block.vent-condenser.description = A kürtőkből kiáramló gázokat vízzé kondenzálja. Áramot igényel. +block.plasma-bore.description = Ércfallal szemben elhelyezve, korlátlanul termel nyersanyagokat. Kis mennyiségű áramot igényel.\nHidrogén felhasználásával növelhető a hatékonysága. +block.large-plasma-bore.description = Egy nagyobb plazmafúró. Képes a volfrám és a tórium bányászatára. Hidrogént és áramot igényel.\nNitrogén felhasználásával növelhető a hatékonysága. +block.cliff-crusher.description = Felőrli a falakat, és korlátlan mennyiségű homokot termel. Áramot igényel. A hatékonysága a fal típusától függően változik. +block.impact-drill.description = Ha ércre helyezik, korlátlan ideig, sorozatokban termeli ki a nyersanyagokat. Áramot és vizet igényel. +block.eruption-drill.description = Továbbfejlesztett ütvefúró. Képes tóriumot bányászni. Hidrogént igényel. +block.reinforced-conduit.description = Előre szállítja a folyadékokat. Nem fogad nem csővezetékes bemeneteket oldalról. block.reinforced-liquid-router.description = Egyenletesen osztja el a folyadékokat minden oldalra. block.reinforced-liquid-tank.description = Nagy mennyiségű folyadékot tárol. block.reinforced-liquid-container.description = Jelentős mennyiségű folyadékot tárol. -block.reinforced-bridge-conduit.description = Folyadékok szállítására alkalmas épületek és terepakadályok fölött. -block.reinforced-pump.description = Folyadékokat szivattyúz és ad ki. Hidrogént igényel. +block.reinforced-bridge-conduit.description = Folyadékot szállít épületek és terepakadályok fölött. +block.reinforced-pump.description = Folyadékot szivattyúz és ad ki. Hidrogént igényel. block.beryllium-wall.description = Megvédi az építményeket az ellenséges lövedékektől. block.beryllium-wall-large.description = Megvédi az építményeket az ellenséges lövedékektől. block.tungsten-wall.description = Megvédi az építményeket az ellenséges lövedékektől. @@ -2184,212 +2184,212 @@ block.carbide-wall.description = Megvédi az építményeket az ellenséges löv block.carbide-wall-large.description = Megvédi az építményeket az ellenséges lövedékektől. block.reinforced-surge-wall.description = Megvédi az építményeket az ellenséges lövedékektől, a lövedékekkel való érintkezésekor időszakosan elektromos íveket bocsát ki. block.reinforced-surge-wall-large.description = Megvédi az építményeket az ellenséges lövedékektől, a lövedékekkel való érintkezésekor időszakosan elektromos íveket bocsát ki. -block.shielded-wall.description = Megvédi az építményeket az ellenséges lövedékektől. Olyan pajzsot képez, amely a legtöbb lövedéket elnyeli, ha Áramot kap. Vezeti az Áramot. -block.blast-door.description = Egy fal, amely kinyílik, ha a szövetséges földi egységek vannak a közelében. Kézzel nem irányítható. +block.shielded-wall.description = Megvédi az építményeket az ellenséges lövedékektől. Olyan pajzsot képez, amely a legtöbb lövedéket elnyeli, ha az áramellátás biztosítva van. Vezeti az áramot. +block.blast-door.description = Egy fal, amely kinyílik, ha szövetséges földi egységek vannak a közelében. Kézzel nem irányítható. block.duct.description = Előre mozgatja a nyersanyagokat. Csak egyetlen nyersanyag tárolására alkalmas. block.armored-duct.description = Előre mozgatja a nyersanyagokat. Csak szállítószalagos bemeneteket fogad el oldalról. -block.duct-router.description = A nyersanyagokat egyenlően osztja el három irányba. Csak a hátsó oldalról fogadja be a tárgyakat. Nyersanyag Válogatóként is konfigurálható. -block.overflow-duct.description = Csak akkor ad ki nyersanyagot oldalra, ha előrefelé már nem tud. Nem használható közvetlenül Túlfolyó Kapu, vagy Alul-folyó Kapu mellett. -block.duct-bridge.description = Nyersanyagok szállítására alkalmas épületek és terepakadályok fölött. -block.duct-unloader.description = A kiválasztott nyersanyagokat kirakodja a mögötte lévő épületekből. Támaszpontokból nem lehet kirakodni. -block.underflow-duct.description = Csak akkor enged tovább nyersanyagokat előre, ha oldalra már nem tudja kiadni őket. Nem használható közvetlenül Túlfolyó Kapu, vagy Alul-folyó Kapu mellett. -block.reinforced-liquid-junction.description = Két egymást keresztező csővezeték közötti csomópontként működik. -block.surge-conveyor.description = A nyersanyagokat rakományokban mozgatja. Árammal felgyorsítható. Vezeti az Áramot. -block.surge-router.description = Egyenletesen osztja el a nyersanyagokat három irányba a Szállítószalagról. Árammal felgyorsítható. Vezeti az Áramot. +block.duct-router.description = A nyersanyagokat egyenlően osztja el három irányba. Csak hátulról fogad el nyersanyagokat. Nyersanyagválogatóként is beállítható. +block.overflow-duct.description = Csak akkor ad ki nyersanyagot oldalra, ha előrefelé már nem tud. +block.duct-bridge.description = Nyersanyagokat szállít épületek és terepakadályok fölött. +block.duct-unloader.description = A kiválasztott nyersanyagokat kirakodja a mögötte lévő épületekből. Támaszpontokból nem tud kirakodni. +block.underflow-duct.description = A túlcsorduló szállítószalag ellentettje. Csak akkor ad ki nyersanyagot előrefelé, ha oldalra már nem tud. +block.reinforced-liquid-junction.description = Csomópontként működik két egymást keresztező csővezeték között. +block.surge-conveyor.description = A nyersanyagokat rakományokban mozgatja. Árammal felgyorsítható. Vezeti az áramot. +block.surge-router.description = Egyenletesen osztja el a nyersanyagokat három irányba az elektrometál-szállítószalagról. Árammal felgyorsítható. Vezeti az Áramot. block.unit-cargo-loader.description = Teherszállító drónokat épít. A drónok automatikusan szétosztják a nyersanyagokat a megfelelő szűrővel rendelkező kirakodási pontokra. block.unit-cargo-unload-point.description = A teherszállító drónok kirakodási pontjaként működik. Csak a kiválasztott szűrőnek megfelelő nyersanyagokat fogadja be. -block.beam-node.description = Az Áramot merőlegesen vezeti a többi épülethez. Kis mennyiségű energiát tárol. -block.beam-tower.description = Az Áramot merőlegesen vezeti a többi épülethez. Nagy mennyiségű energiát tárol. Nagy hatótávolságú. -block.turbine-condenser.description = A kürtőkből kiáramló gázokból Áramot termel. Kis mennyiségű Vizet termel. -block.chemical-combustion-chamber.description = Arkycitből és Ózonból termel Áramot. -block.pyrolysis-generator.description = Nagy mennyiségű Áramot termel Arkycitből és Salakból. Melléktermékként Vizet termel. -block.flux-reactor.description = Nagy mennyiségű Áramot termel Hő hatására. Stabilizátorként Cianogént igényel. Az Áramtermelés és a Cianogén igény arányos a Hőbevitellel.\nFelrobban, ha nem áll rendelkezésre elegendő Cianogén. -block.neoplasia-reactor.description = Arkycit, Víz és Tóritkvarc felhasználásával nagy mennyiségű Áramot termel. Melléktermékként Hőt és veszélyes Neoplazmát termel.\nHevesen robban, ha a Neoplazmát nem távolítják el a reaktorból a csővezetékeken keresztül. +block.beam-node.description = Merőlegesen áramot vezet a többi blokkhoz. Kis mennyiségű áramot tárol. +block.beam-tower.description = Merőlegesen áramot vezet a többi blokkhoz. Nagy mennyiségű áramot tárol. Nagy hatótávolságú. +block.turbine-condenser.description = Kürtőkre helyezve áramot termel. Kis mennyiségű vizet termel. +block.chemical-combustion-chamber.description = Áramot termel arkicitből és ózonból. +block.pyrolysis-generator.description = Nagy mennyiségű áramot termel arkicitből és salakból. Melléktermékként vizet termel. +block.flux-reactor.description = Fűtés hatására nagy mennyiségű áramot termel. Stabilizátorként diciánt igényel. Az áramtermelés és a diciánigény arányos a hőbevitellel.\nFelrobban, ha nem áll rendelkezésre elegendő dicián. +block.neoplasia-reactor.description = Arkicit, víz és tóritkvarc felhasználásával nagy mennyiségű áramot termel. Melléktermékként hőt és veszélyes neoplazmát termel.\nFelrobban, ha a neoplazmát nem távolítják el a reaktorból csővezetékeken keresztül. block.build-tower.description = Automatikusan újjáépíti a hatósugarában lévő építményeket, és segíti a többi egységet az építkezésben. -block.regen-projector.description = Lassan javítja a szövetséges építményeket egy négyzet alakú kerületben. Hidrogént igényel. -block.reinforced-container.description = Kis mennyiségű nyersanyagot tud tárolni. A tartalma kirakodók segítségével nyerhető ki. Nem növeli a Támaszpont tárolókapacitását. -block.reinforced-vault.description = Nagy mennyiségű nyersanyagot tud tárolni. A tartalma kirakodók segítségével nyerhető ki. Nem növeli a Támaszpont tárolókapacitását. -block.tank-fabricator.description = Stell egységeket épít. A kimeneti egységek közvetlenül felhasználhatóak, vagy átvihetőek Tank Újratervezőbe fejlesztésre. -block.ship-fabricator.description = Elude egységeket épít. A kimeneti egységek közvetlenül felhasználhatóak, vagy átvihetőek Repülőgép Újratervezőbe fejlesztésre. -block.mech-fabricator.description = Merui egységeket épít. A kimeneti egységek közvetlenül felhasználhatóak, vagy átvihetőek Mech Újratervezőbe fejlesztésre. -block.tank-assembler.description = Nagy méretű tankokat állít össze a beadott blokkokból és egységekből. A kimeneti szint Összeszerelő Modulok hozzáadásával növelhető. -block.ship-assembler.description = Nagy méretű hajókat állít össze a beadott blokkokból és egységekből. A kimeneti szint Összeszerelő Modulok hozzáadásával növelhető. -block.mech-assembler.description = Nagy méretű Mech-eket állít össze a beadott blokkokból és egységekből. A kimeneti szint Összeszerelő Modulok hozzáadásával növelhető. -block.tank-refabricator.description = Felfejleszti a bejuttatott tank típusú egységeket a második szintre. -block.ship-refabricator.description = Felfejleszti a bejuttatott repülőgép típusú egységeket a második szintre. -block.mech-refabricator.description = Felfejleszti a bejuttatott mech típusú egységeket a második szintre. -block.prime-refabricator.description = A bejuttatott tank-/repülőgép-/mech típusú egységeket a harmadik szintre fejleszti. -block.basic-assembler-module.description = Növeli a Tank-/Repülgép-/Mech Összeszerelő szintjét, ha annak az építési határvonala mellé helyezik. Áramot igényel. Használható nyersanyag-rakomány bemenetként. -block.small-deconstructor.description = Lebontja a bevitt építményeket és egységeket. Visszaadja az építési költség 100%-át. +block.regen-projector.description = Lassan javítja a szövetséges építményeket egy négyzet alakú területen. Hidrogént igényel.\nTóritkvarc felhasználásával növelhető a hatékonysága. +block.reinforced-container.description = Kis mennyiségű nyersanyagot tud tárolni. A tartalma kirakodók segítségével nyerhető ki. Nem növeli a támaszpont tárolókapacitását. +block.reinforced-vault.description = Nagy mennyiségű nyersanyagot tud tárolni. A tartalma kirakodók segítségével nyerhető ki. Nem növeli a támaszpont tárolókapacitását. +block.tank-fabricator.description = Stell egységeket épít. A kimeneti egységek közvetlenül használhatók, vagy fejlesztésre újratervezőkbe küldhetők. +block.ship-fabricator.description = Elude egységeket épít. A kimeneti egységek közvetlenül használhatók, vagy fejlesztésre újratervezőkbe küldhetők. +block.mech-fabricator.description = Merui egységeket épít. A kimeneti egységek közvetlenül használhatók, vagy fejlesztésre újratervezőkbe küldhetők. +block.tank-assembler.description = Nagy méretű tankokat állít össze a beadott blokkokból és egységekből. A kimeneti szint modulok hozzáadásával növelhető. +block.ship-assembler.description = Nagy méretű hajókat állít össze a beadott blokkokból és egységekből. A kimeneti szint modulok hozzáadásával növelhető. +block.mech-assembler.description = Nagy méretű mecheket állít össze a beadott blokkokból és egységekből. A kimeneti szint modulok hozzáadásával növelhető. +block.tank-refabricator.description = Kettes szintre fejleszti a beérkező tank típusú egységeket. +block.ship-refabricator.description = Kettes szintre fejleszti a beérkező hajó típusú egységeket. +block.mech-refabricator.description = Kettes szintre fejleszti a beérkező mech típusú egységeket. +block.prime-refabricator.description = Hármas szintre fejleszti a beérkező tank típusú egységeket. +block.basic-assembler-module.description = Növeli az összeszerelő szintjét, ha annak az építési határvonala mellé helyezik. Áramot igényel. Használható nyersanyagrakomány-bemenetként. +block.small-deconstructor.description = Lebontja a beadott építményeket és egységeket. Visszaadja az építési költség 100%-át. block.reinforced-payload-conveyor.description = Előre mozgatja a rakományokat. -block.reinforced-payload-router.description = A rakományokat szomszédos a blokkokba osztja szét. Szűrő beállítása esetén válogatóként is funkcionál. -block.payload-mass-driver.description = Nagy hatótávolságú nyersanyagszállító eszköz. Rakományokban lő át nyersanyagokat egy másik, hozzákapcsolt Tömegmozgatónak. -block.large-payload-mass-driver.description = Nagy hatótávolságú rakományszállító szerkezet. A fogadott rakományokat a hozzákapcsolt másik Tömegmozgatóra lövi. +block.reinforced-payload-router.description = A rakományokat szomszédos a blokkokba osztja szét. Szűrő beállítása esetén válogatóként működik. +block.payload-mass-driver.description = Nagy hatótávolságú rakományszállító épület. A kapott rakományokat egy másik, hozzákapcsolt rakomány-tömegmozgatónak lövi át. +block.large-payload-mass-driver.description = Nagy hatótávolságú rakományszállító épület. A kapott rakományokat egy másik, hozzákapcsolt rakomány-tömegmozgatónak lövi át. block.unit-repair-tower.description = Javítja a közelében lévő összes egységet. Ózont igényel. block.radar.description = Fokozatosan feltárja a terepet és az ellenséges egységeket egy nagy sugarú körben. Áramot igényel. -block.shockwave-tower.description = Sérülést okoz és megsemmisíti az ellenséges lövedékeket egy egységsugarú körön belül. Cianogént igényel. +block.shockwave-tower.description = Sérülést okoz és megsemmisíti az ellenséges lövedékeket egy körön belül. Diciánt igényel. block.canvas.description = Egy egyszerű képet jelenít meg egy előre meghatározott palettával. Szerkeszthető. -unit.dagger.description = Egyszerű töltényeket lő közeli ellenségekre +unit.dagger.description = Szokásos lövedékeket lő a közeli ellenségekre. unit.mace.description = Lángnyelveket küld a közeli ellenségek felé. unit.fortress.description = Nagy hatótávú rakétákat lő földi célpontokra. unit.scepter.description = Töltött lövedékek záporát lövi közeli ellenségekre. unit.reign.description = Méretes átütő erejű lövedékeket zúdít minden közeli ellenségre. -unit.nova.description = Lézereket lő, amelyek az ellenséget sebzik, de gyógyítják a szövetségeseket. Repülésre alkalmas. -unit.pulsar.description = Elektromos szikrákat szór, amelyek az ellenséget sebzik, de gyógyítják a szövetségeseket. Repülésre alkalmas. -unit.quasar.description = Átütő erejű lézersugarakat lő, amelyek az ellenséget sebzik, de gyógyítják a szövetségeseket. Repülésre alkalmas. Pajzsa van. -unit.vela.description = Folyamatos lézernyalábot bocsát ki, ami sebzi az ellenséget, felgyújtja az épületeiket, de gyógyítja a szövetségeseket. Repülésre alkalmas. -unit.corvus.description = Hatalmas lézersugarat lő, amelyek sebzik az ellenséget, de gyógyítják a szövetségeseket. A legtöbb terepakadályt átlépi. -unit.crawler.description = Az ellenséghez rohan és egy nagy robbanásban megsemmisíti önmagát, így sebezve az ellenséget. -unit.atrax.description = Gyengítő Salakgolyókat lő a földi célpontokra. A legtöbb terepakadályt átlépi. -unit.spiroct.description = Elszívja az ellenség életerejét, önmagát gyógyítva közben. A legtöbb terepakadályt átlépi. -unit.arkyid.description = Nagy lézernyalábokkal elszívja az ellenség életerejét, önmagát gyógyítva közben. A legtöbb terepakadályt átlépi. -unit.toxopid.description = Nagy elektromos bombákat és átütő erejű lézert lő az ellenségre. A legtöbb terepakadályt átlépi. -unit.flare.description = Egyszerű töltényeket lő közeli földi célpontokra. -unit.horizon.description = Bombákat szór földi célpontokra. +unit.nova.description = Lézerlövedékeket lő ki, amelyek sebzik az ellenséges célpontokat, és megjavítják a szövetséges épületeket. Repülésre alkalmas. +unit.pulsar.description = Elektromos szikrákat szór, amelyek sebzik az ellenséges célpontokat, és megjavítják a szövetséges épületeket. Repülésre alkalmas. +unit.quasar.description = Átütő erejű lézersugarakat lő, amelyek sebzik az ellenséges célpontokat, és megjavítják a szövetséges épületeket. Repülésre alkalmas. Pajzsa van. +unit.vela.description = Folyamatos lézernyalábot bocsát ki, amelyek sebzik az ellenséges célpontokat, tüzet okoznak, és megjavítják a szövetséges épületeket. Repülésre alkalmas. +unit.corvus.description = Hatalmas lézersugarat lő, amelyek sebzik az ellenséges célpontokat, és megjavítják a szövetséges épületeket. Átlépi a legtöbb terepakadályt. +unit.crawler.description = Az ellenséghez rohan és megsemmisíti önmagát, nagy robbanást okozva. +unit.atrax.description = Bénító salakgolyókat lő a földi célpontokra. A legtöbb terepakadályt átlépi. +unit.spiroct.description = Lézersugarakkal elszívja az ellenséges célpontok életerejét, miközben javítja önmagát. A legtöbb terepakadályt átlépi. +unit.arkyid.description = Nagy lézersugarakkal elszívja az ellenséges célpontok életerejét, miközben javítja önmagát. A legtöbb terepakadályt átlépi. +unit.toxopid.description = Kazettás elektromos bombákat és átütő erejű lézert lő az ellenségre. A legtöbb terepakadályt átlépi. +unit.flare.description = Szokásos lövedékeket lő közeli földi célpontokra. +unit.horizon.description = Kazettás bombákat szór földi célpontokra. unit.zenith.description = Rakétasorozatokat lő közeli ellenségekre. unit.antumbra.description = Lövedékek záporát zúdítja minden közeli ellenségre. unit.eclipse.description = Két átütő erejű lézersugarat és rengeteg lövedéket zúdít minden közeli ellenségre. -unit.mono.description = Automatikusan bányászik Rezet és Ólmot a Támaszpontba juttatva őket. -unit.poly.description = Automatikusan újjáépíti az elpusztult épületeket és segít más egységeknek építkezni. -unit.mega.description = Automatikusan javítja a sérült épületeket. Képes kis épületek és földi egységek szállítására. -unit.quad.description = Nagy bombákat szór földi célpontokra, amelyek sebzik az ellenséget, de javítják a szövetséges épületeket. Képes közepes méretű földi egységek szállítására. -unit.oct.description = Megvédi a közeli szövetségeseket regeneráló pajzsával. Képes szállítani a legtöbb földi egységet. +unit.mono.description = Automatikusan rezet és ólmot bányászik, a támaszpontba juttatva őket. +unit.poly.description = Automatikusan újjáépíti az elpusztult épületeket és segít más egységeknek az építkezésben. +unit.mega.description = Automatikusan javítja a sérült épületeket. Kis blokkok és földi egységek szállítására képes. +unit.quad.description = Plazmabombákat szór földi célpontokra, amelyek sebzik az ellenséget, de javítják a szövetséges épületeket. Közepes méretű földi egységek szállítására képes. +unit.oct.description = Megvédi a közeli szövetségeseket egy regeneráló pajzssal. A legtöbb földi egység szállítására képes. unit.risso.description = Rakéták és lövedékek záporát zúdítja minden közeli ellenségre. -unit.minke.description = Tüzérségi lövedékeket és egyszerű töltényeket lő közeli földi célpontokra. -unit.bryde.description = Nagy távolságú tüzérségi rakétákat lő az ellenségre. +unit.minke.description = Tüzérségi és szokásos lövedékeket lő közeli földi célpontokra. +unit.bryde.description = Nagy távolságú tüzérségi lövedékeket és rakétákat lő az ellenségre. unit.sei.description = Rakéták és páncéltörő lövedékek záporát zúdítja az ellenségre. unit.omura.description = Nagy hatótávolságú átütő erejű lövedékeket lő az ellenségre. Flare egységeket gyárt. -unit.alpha.description = Megvédi a Bástyát az ellenségtől. Épít. -unit.beta.description = Megvédi az Alapítványt az ellenségtől. Épít. -unit.gamma.description = Megvédi a Magnumot az ellenségtől. Épít. +unit.alpha.description = Megvédi a Szilánk támaszpontot az ellenségtől. Épületeket épít. +unit.beta.description = Megvédi az Alapítvány támaszpontot az ellenségtől. Épületeket épít. +unit.gamma.description = Megvédi at Atommag támaszpontot az ellenségtől. Épületeket épít. unit.retusa.description = Célkövető torpedókat lő ki minden közeli ellenségre. Javítja a szövetséges egységeket. -unit.oxynoe.description = Plazma lángcsóvát lő az ellenséges célpontokra. Célba veszi az ellenséges lövedékeket egy pontvédelmi toronnyal. -unit.cyerce.description = Célpont kereső kazettás rakétákat lő ki az ellenségre. Javítja a szövetséges egységeket. +unit.oxynoe.description = Épületjavító lángcsóvát lő az ellenséges célpontokra. Célba veszi az ellenséges lövedékeket egy pontvédelmi toronnyal. +unit.cyerce.description = Célkereső kazettás rakétákat lő ki az ellenségre. Javítja a szövetséges egységeket. unit.aegires.description = Elektromosan sokkolja az összes ellenséges egységet és építményt, amely az energiamezőjébe lép. Javítja az összes szövetségest. -unit.navanax.description = Robbanékony EMP lövedékeket lő ki, jelentős károkat okozva az ellenséges energiahálózatokban és megjavítva a szövetségesek építményeit. Megolvasztja a közeli ellenséget 4 autonóm lézertoronnyal. +unit.navanax.description = Robbanékony EMI-lövedékeket lő ki, jelentős károkat okozva az ellenséges energiahálózatokban, és megjavítva a szövetségesek építményeit. Szétolvasztja a közeli ellenséges célpontokat a 4 autonóm lézertornyával. #Erekir -unit.stell.description = Normál lövedékeket lő ki az ellenséges célpontokra. +unit.stell.description = Szokásos lövedékeket lő ki az ellenséges célpontokra. unit.locus.description = Változatos lövedékeket lő ki az ellenséges célpontokra. unit.precept.description = Átütő erejű kazettás lövedékeket lő ki az ellenséges célpontokra. -unit.vanquish.description = Nagy átütési képességű, hasítólövedékeket lő ki az ellenséges célpontokra. -unit.conquer.description = Nagy átütési képességű golyózáport lő ki az ellenséges célpontokra. +unit.vanquish.description = Nagy, átütő erejű hasítólövedékeket lő ki az ellenséges célpontokra. +unit.conquer.description = Nagy, átütő erejű golyózáport lő ki az ellenséges célpontokra. unit.merui.description = Nagy hatótávolságú ágyúkkal lövi az ellenséges szárazföldi célpontokat. A legtöbb terepakadályt átlépi. unit.cleroi.description = Kettős lövedékeket lő ki az ellenséges célpontokra. Célba veszi az ellenséges lövedékeket a pontvédelmi tornyokkal. A legtöbb terepakadályt átlépi. unit.anthicus.description = Nagy hatótávolságú célkövető rakétákat lő ki az ellenséges célpontokra. A legtöbb terepakadályt átlépi. -unit.tecta.description = Célkövető plazma rakétákat lő ki az ellenséges célpontokra. Irányított pajzzsal védi magát. A legtöbb terepakadályt átlépi. +unit.tecta.description = Célkövető plazmarakétákat lő ki az ellenséges célpontokra. Irányított pajzzsal védi magát. A legtöbb terepakadályt átlépi. unit.collaris.description = Nagy hatótávolságú repeszágyúval lövi az ellenséges célpontokat. A legtöbb terepakadályt átlépi. -unit.elude.description = Célkövető golyópárokat lő ki az ellenséges célpontokra. Képes folyadéktestek felett lebegni. +unit.elude.description = Célkövető golyópárokat lő ki az ellenséges célpontokra. Lebeg a folyadékfelületeket felett. unit.avert.description = Forgó lövedékpárokat lő ki az ellenséges célpontokra. unit.obviate.description = Forgó, páros villámgömböket lő ki az ellenséges célpontokra. -unit.quell.description = Kettő darab, nagy hatótávolságú célkövető rakétát lő ki egyszerre az ellenséges célpontokra. Elnyomja az ellenséges szerkezetjavító épületeket. -unit.disrupt.description = Három darab, nagy hatótávolságú célkövető rakétát lő ki egyszerre az ellenséges célpontokra. Elnyomja az ellenséges szerkezetjavító épületeket. -unit.evoke.description = A Bástya védelmére szolgáló építményeket épít. Sugárral javítja az építményeket. -unit.incite.description = A Citadella védelmére szolgáló építményeket épít. Sugárral javítja az építményeket. -unit.emanate.description = Az Akropolisz védelmére szolgáló építményeket épít. Sugárral javítja az építményeket. +unit.quell.description = Nagy hatótávolságú célkövető rakétát lő ki az ellenséges célpontokra. Elnyomja az ellenséges szerkezetjavító épületeket. Csak földi célpontokat támad. +unit.disrupt.description = Nagy hatótávolságú célkövető elnyomó rakétát lő ki az ellenséges célpontokra. Elnyomja az ellenséges szerkezetjavító épületeket. Csak földi célpontokat támad. +unit.evoke.description = A Bástya védelmére szolgáló építményeket épít. Sugárral javítja az építményeket. 2×2-es épületek szállítására képes. +unit.incite.description = A Citadella védelmére szolgáló építményeket épít. Sugárral javítja az építményeket. 2×2-es épületek szállítására képes. +unit.emanate.description = Az Akropolisz védelmére szolgáló építményeket épít. Sugárral javítja az építményeket. 2×2-es épületek szállítására képes. lst.read = Szám kiolvasása egy összekapcsolt memóriacellából. -lst.write = Írj egy számot egy összekapcsolt memóriacellába. -lst.print = Szöveg hozzáadása a nyomtatási pufferhez.\nNem jelenít meg semmit, amíg a [accent]Kiírási Művelet[] használatban van. -lst.format = A szövegpufferben lévő következő helyőrzőt egy értékkel helyettesíti.\nNem csinál semmit, ha a helyőrző minta érvénytelen.\nHelyőrző minta: "{[accent]number 0-9[]}"\nPélda:\n[accent]print "test {0}"\nformat "example" -lst.draw = Művelet hozzáadása a rajzpufferhez.\nNem jelenít meg semmit, amíg a [accent]Rajzolási Művelet[] használatban van. -lst.drawflush = Sorba állított [accent]rajz[] műveletek megjelenítése a kijelzőn. -lst.printflush = Sorba állított [accent]kiírási[] műveletek kiírása egy üzenetblokkba. -lst.getlink = A processzor linkjének lekérdezése index szerint. 0-nál kezdődik. +lst.write = Szám beírása egy összekapcsolt memóriacellába. +lst.print = Szöveg hozzáadása a nyomtatási pufferhez.\nA [accent]Print Flush[] használatáig nem jelenít meg semmit. +lst.format = A szövegpufferben lévő következő helyőrző cseréje egy értékre.\nNem csinál semmit, ha a helyőrzőminta érvénytelen.\nHelyőrzőminta: "{[accent]number 0-9[]}"\nPélda:\n[accent]print "test {0}"\nformat "example" +lst.draw = Művelet hozzáadása a rajzpufferhez.\nA [accent]Draw Flush[] használatáig nem jelenít meg semmit. +lst.drawflush = Sorba állított [accent]Draw[] műveletek megjelenítése a kijelzőn. +lst.printflush = Sorba állított [accent]Print[] műveletek kiírása egy üzenetblokkba. +lst.getlink = A processzor hivatkozásának lekérése index alapján. 0-nál kezdődik. lst.control = Épület irányítása. -lst.radar = Beméri az egységeket az épület körül egy bizonyos hatótávolságban. +lst.radar = Egységek megkeresése az épület körül egy bizonyos hatótávolságban. lst.sensor = Adatok lekérése egy épületről vagy egységről. lst.set = Egy változó beállítása. -lst.operation = Végezzen el egy műveletet 1-2 változóval. +lst.operation = Egy művelet elvégzése 1-2 változóval. lst.end = Ugrás az utasítási verem tetejére. -lst.wait = Várjon bizonyos számú másodpercet. -lst.stop = A processzor program futtatásának leállítása. -lst.lookup = Egy nyersanyag/folyadék/egység/épület típus keresése azonosító alapján.\nAz egyes típusok összesített száma a következőkkel érhető el:\n[accent]@unitCount[] / [accent]@itemCount[] / [accent]@liquidCount[] / [accent]@blockCount[] +lst.wait = Várakozás bizonyos számú másodpercig. +lst.stop = A processzor futtatásának leállítása. +lst.lookup = Egy nyersanyag/folyadék/egység/épület típusának keresése azonosító alapján.\nAz egyes típusok összesített száma a következőkkel érhető el:\n[accent]@unitCount[] / [accent]@itemCount[] / [accent]@liquidCount[] / [accent]@blockCount[]\nA fordított művelethez használd az objektum [accent]@id[] értékét. lst.jump = Feltételes ugrás egy másik utasításra. -lst.unitbind = Összekapcsolás a következő egységtípussal és tárolja a következőben: [accent]@unit[]. -lst.unitcontrol = Az aktuálisan összekapcsolt egység vezérlése. -lst.unitradar = Egységek keresése az aktuálisan összekapcsolt egység körül. +lst.unitbind = Összekapcsolás a következő egységtípussal, és tárolás a következőben: [accent]@unit[]. +lst.unitcontrol = A jelenleg összekapcsolt egység vezérlése. +lst.unitradar = Egységek keresése a jelenleg összekapcsolt egység körül. lst.unitlocate = Egy adott típusú pozíció/épület keresése bárhol a pályán.\nÖsszekapcsolt egységet igényel. lst.getblock = Csempeadatok lekérdezése tetszőleges helyen. lst.setblock = Csempeadatok beállítása tetszőleges helyen. -lst.spawnunit = Egység lerakása egy helyen. -lst.applystatus = Állapothatás alkalmazása, vagy törlése egy egységről. -lst.weathersensor = Check if a type of weather is active. -lst.weatherset = Set the current state of a type of weather. -lst.spawnwave = Egy tetszőleges helyen keletkező hullám szimulálása.\nNem növeli a hullámszámlálót. -lst.explosion = Robbanás létrehozása egy helyen. -lst.setrate = A processzor végrehajtási sebességének beállítása utasítás/ütemben. -lst.fetch = Egységek, Támaszpontok, játékosok, vagy épületek keresése index szerint.\nAz indexek 0-nál kezdődnek és a visszaadott számuknál végződnek. -lst.packcolor = Tömöríti [0, 1] az RGBA komponenseket egyetlen számba a rajzoláshoz, vagy szabálymeghatározáshoz. -lst.setrule = Állíts be egy játékszabályt. +lst.spawnunit = Egység lerakása az adott helyen. +lst.applystatus = Állapothatás alkalmazása vagy törlése egy egységről. +lst.weathersensor = Ellenőrzés, hogy egy adott időjárástípus aktív-e. +lst.weatherset = Az időjárástípus jelenlegi állapotának megadása. +lst.spawnwave = Egy hullám indítása. +lst.explosion = Robbanás létrehozása az adott helyen. +lst.setrate = A processzor végrehajtási sebességének beállítása utasítás/órajelciklusban. +lst.fetch = Egységek, támaszpontok, játékosok, vagy épületek keresése index szerint.\nAz indexek 0-tól indulnak és a visszaadott számuknál végződnek. +lst.packcolor = Egyetlen számba tömöríti a [0, 1] RGBA komponenseket a rajzoláshoz vagy szabálymeghatározáshoz. +lst.setrule = Játékszabály beállítása. lst.flushmessage = Üzenet megjelenítése a képernyőn a szövegpufferből.\nMegvárja, amíg az előző üzenet befejeződik. -lst.cutscene = Manipulálja a játékos kameráját. +lst.cutscene = A játékos kamerájának mozgatása. lst.setflag = Egy globális jelölő beállítása, amely minden processzor által olvasható. -lst.getflag = Ellenőrzi, hogy egy globális jelölő be van-e állítva. -lst.setprop = Egy egység vagy épület tulajdonságát állítja be. -lst.effect = Létrehoz egy részecske hatást. -lst.sync = Egy változó szinkronizálása a hálózaton keresztül.\nMásodpercenként legfeljebb 10 alkalommal hívható meg. -lst.makemarker = Új logikai jelölő létrehozása a világban.\nEgy azonosítót kell megadni a jelölő azonosításához.\nA jelölők száma jelenleg világonként 20 000-re van korlátozva. -lst.setmarker = Egy jelölő tulajdonságának beállítása.\nA használt azonosítónak meg kell egyeznie a Make Marker utasításban megadottal.\n[accent]null []értékek figyelmen kívül lesznek hagyva. -lst.localeprint = Hozzáadja a pálya Helyi Csomagjainak tulajdonság értékét a szövegpufferhez.\nA pálya Helyi Csomagjainak beállításait a térképszerkesztőben ellenőrizheted [accent]Pálya Infó > Helyi Csomagok[].\nHa a kliens egy mobileszköz, akkor először próbáld kiíratni a ".mobile" végződésű tulajdonságot. +lst.getflag = Ellenőrzés, hogy egy globális jelölő be van-e állítva. +lst.setprop = Beállítja egy egység vagy épület tulajdonságát. +lst.effect = Részecskehatás létrehozása. +lst.sync = Egy változó szinkronizálása a hálózaton keresztül.\nMásodpercenként legfeljebb 20-szor hívható meg. +lst.makemarker = Új logikai jelölő létrehozása a világban.\nMeg kell adni egy azonosítót a jelölő azonosításához.\nA jelölők száma jelenleg világonként 20 000-re van korlátozva. +lst.setmarker = Egy jelölő tulajdonságának beállítása.\nA használt azonosítónak meg kell egyeznie a Make Marker utasításban megadottal.\nA [accent]null []értékek figyelmen kívül lesznek hagyva. +lst.localeprint = Hozzáadja a pálya nyelvi csomagjainak tulajdonságértékét a szövegpufferhez.\nA pálya nyelvi csomagjainak beállításait a térképszerkesztőben ellenőrizheted: [accent]Pályainformációk > Nyelvi csomagok[].\nHa a kliens egy mobileszköz, akkor először próbáld kiíratni a „.mobile” végződésű tulajdonságot. lglobal.false = 0 lglobal.true = 1 -lglobal.null = nulla -lglobal.@pi = A pi matematikai állandó (3.141...) +lglobal.null = null +lglobal.@pi = A pí matematikai állandó (3.141...) lglobal.@e = Az e matematikai állandó (2.718...) -lglobal.@degToRad = Szorozza meg ezt a számot a fokok radiánra való konvertálásához -lglobal.@radToDeg = Szorozza meg ezt a számot a radiánok fokokká konvertálásához +lglobal.@degToRad = Ezzel a számmal szoroz a fok radiánra való átalakításához +lglobal.@radToDeg = Ezzel a számmal szoroz a radián fokra való átalakításához -lglobal.@time = Az aktuális mentés játékideje, milliszekundumban -lglobal.@tick = Az aktuális mentés játékideje, tick-ben (1 másodperc = 60 tick) -lglobal.@second = Az aktuális mentés játékideje, másodpercben -lglobal.@minute = Az aktuális mentés játékideje, percben -lglobal.@waveNumber = Az aktuális hullám száma, ha a hullámok engedélyezve vannak +lglobal.@time = A jelenelgi mentés játékideje ezredmásodpercben +lglobal.@tick = Az aktuális mentés játékideje, órajelciklusban (1 másodperc = 60 órajelciklus) +lglobal.@second = A jelenelgi mentés játékideje másodpercben +lglobal.@minute = A jelenelgi mentés játékideje percben +lglobal.@waveNumber = A jelenlegi hullám száma, ha a hullámok engedélyezve vannak lglobal.@waveTime = Visszaszámláló a hullámokhoz, másodpercben lglobal.@mapw = A pálya szélessége csempékben lglobal.@maph = A pálya magassága csempékben lglobal.sectionMap = Pálya lglobal.sectionGeneral = Általános -lglobal.sectionNetwork = Hálózati/kliensoldali [Csak Világprocesszor] +lglobal.sectionNetwork = Hálózati/kliensoldali [Csak világprocesszor] lglobal.sectionProcessor = Processzor -lglobal.sectionLookup = Keres +lglobal.sectionLookup = Keresés lglobal.@this = A kódot végrehajtó logikai blokk lglobal.@thisx = A kódot végrehajtó blokk X koordinátája lglobal.@thisy = A kódot végrehajtó blokk Y koordinátája -lglobal.@links = Az ehhez a processzorhoz kapcsolt épületek összesített száma -lglobal.@ipt = A processzor utasítás végrehajtási sebessége tick-ben (60 tick = 1 másodperc) +lglobal.@links = Az ehhez a processzorhoz kapcsolt épületek száma összesen +lglobal.@ipt = A processzor végrehajtási sebessége órajelciklusban (60 órajelciklus = 1 másodperc) -lglobal.@unitCount = A játékban található egységek típusainak összesített száma; a Keres utasítással együtt használjuk -lglobal.@blockCount = A játékban található blokkok típusainak összesített száma; a Keres utasítással együtt használjuk -lglobal.@itemCount = A játékban található nyersanyagok típusainak összesített száma; a Keres utasítással együtt használjuk -lglobal.@liquidCount = A játékban található folyadékok típusainak összesített száma; a Keres utasítással együtt használjuk +lglobal.@unitCount = A játékban található egységtípusok száma összesen; a keresés utasítással együtt használatos +lglobal.@blockCount = A játékban található blokktípusok száma összesen; a keresés utasítással együtt használatos +lglobal.@itemCount = A játékban található nyersanyagtípusok száma összesen; a keres utasítással együtt használatos +lglobal.@liquidCount = A játékban található folyadéktípusok száma összesen; a keresés utasítással együtt használatos lglobal.@server = Igaz, ha a kód egy kiszolgálón, vagy egyjátékos módban fut, egyébként hamis lglobal.@client = Igaz, ha a kód egy kiszolgálóhoz kapcsolódott kliensen fut -lglobal.@clientLocale = A kódot futtató kliens nyelvi beállítása. Például: hu_HU +lglobal.@clientLocale = A kódot futtató kliens területi beállítása. Például: hu_HU lglobal.@clientUnit = A kódot futtató kliens egysége -lglobal.@clientName = A kódot futtató kliens játékos neve -lglobal.@clientTeam = A kódot futtató kliens csapat azonosítója +lglobal.@clientName = A kódot futtató kliens játékosneve +lglobal.@clientTeam = A kódot futtató kliens csapatazonosítója lglobal.@clientMobile = Igaz, ha a kódot futtató kliens egy mobil eszköz, egyébként hamis logic.nounitbuild = [red]Az egységépítési logika itt nem megengedett. -lenum.type = Az épület/egység típusa.\nPéldául bármilyen elosztó esetében ez a [accent]@router[] értéket adja vissza.\nNem karakterlánc. -lenum.shoot = Lőj egy pontra. -lenum.shootp = Lőj egy egységre/épületre sebesség előrejelzéssel. -lenum.config = Épületkonfiguráció, például nyersanyag Válogató. -lenum.enabled = Attól függ, hogy a blokk engedélyezve van-e. +lenum.type = Az épület/egység típusa.\nPéldául bármilyen elosztó esetén a [accent]@router[] értéket adja vissza.\nNem karakterlánc. +lenum.shoot = Lövés egy adott pontra. +lenum.shootp = Lövés egy egységre/épületre sebesség-előrejelzéssel. +lenum.config = Épületkonfiguráció, például nyersanyag-válogató. +lenum.enabled = Engedélyezve van-e a blokk. laccess.color = Megvilágítás színe. -laccess.controller = Egységvezérlő. Ha a processzor vezérli, akkor a processzort adja vissza.\nHa formációban van, akkor a vezérlőjét adja vissza.\nMáskülönben magát az egységet adja vissza. -laccess.dead = Attól függ, hogy egy egység/épület halott-e, vagy már nem érvényes. -laccess.controlled = Visszatér:\n[accent]@ctrlProcessor[]l, ha az egységvezérlő egy processzor\n[accent]@ctrlPlayer[]l, ha az egység/épület vezérlője a játékos\n[accent]@ctrlFormation[]l, ha az egység formációban van\nMáskülönben 0. -laccess.progress = Akció előrehaladása, 0 és 1. között.\nVisszatér a termeléssel, a lövegtorony újratöltésével, vagy az építés előrehaladásával. +laccess.controller = Egységvezérlő. Ha processzor vezérli, akkor a processzort adja vissza.\nKülönben magát az egységet adja vissza. +laccess.dead = Egy épület/egység halott-e, vagy már nem érvényes-e. +laccess.controlled = Ezt adja vissza:\n[accent]@ctrlProcessor[], ha az egységvezérlő egy processzor\n[accent]@ctrlPlayer[], ha az egység/épület vezérlője a játékos\n[accent]@ctrlFormation[], ha az egység formációban van\nKülönben 0. +laccess.progress = Művelet előrehaladása, 0 és 1 között.\nA termelés, a lövegtorony-újratöltés vagy az építés előrehaladását adja vissza. laccess.speed = Az egység legnagyobb sebessége, csempe/mp-ben. laccess.id = Egy egység/blokk/nyersanyag/folyadék azonosítója.\nEz a keresési művelet fordítottja. @@ -2397,47 +2397,47 @@ lcategory.unknown = Ismeretlen lcategory.unknown.description = Nem kategorizált utasítások. lcategory.io = Bemenet és kimenet lcategory.io.description = A memóriablokkok és processzorpufferek tartalmának módosítása. -lcategory.block = Blokk vezérlés +lcategory.block = Blokkvezérlés lcategory.block.description = Interakció a blokkokkal. lcategory.operation = Műveletek lcategory.operation.description = Logikai műveletek. -lcategory.control = Áramlásszabályozás +lcategory.control = Folyamatvezérlés lcategory.control.description = A végrehajtási sorrend kezelése. lcategory.unit = Egységvezérlés -lcategory.unit.description = Adj parancsokat az egységeknek. +lcategory.unit.description = Parancsok adása az egységeknek. lcategory.world = Világ lcategory.world.description = A világ viselkedésének szabályozása. graphicstype.clear = A kijelző kitöltése egy színnel. graphicstype.color = Szín kiválasztása a következő rajzolási műveletekhez. -graphicstype.col = A színnel egyenértékű, de csomagolva.\nA csomagolt színeket HEX kódként írja ki egy [accent]%[] előtaggal.\nPéldául: [accent]%ff0000[] piros lenne. -graphicstype.stroke = Vonalszélesség beállítása. -graphicstype.line = Rajzolj vonalszakaszt. +graphicstype.col = A színnel egyenértékű, de csomagolva van.\nA csomagolt színeket hexa kódként írja ki egy [accent]%[] előtaggal.\nPéldául: a [accent]%ff0000[] piros lenne. +graphicstype.stroke = Vonalvastagság beállítása. +graphicstype.line = Vonalszakasz rajzolása. graphicstype.rect = Téglalap kitöltése. -graphicstype.linerect = Rajzolj egy téglalap alakú körvonalat. +graphicstype.linerect = Téglalap körvonalának rajzolása. graphicstype.poly = Egy szabályos sokszög kitöltése. -graphicstype.linepoly = Rajzolj egy szabályos sokszög körvonalat. -graphicstype.triangle = Töltsön ki egy háromszöget. -graphicstype.image = Rajzolj egy képet valamilyen tartalomról.\nPédául: [accent]@router[], vagy [accent]@dagger[]. -graphicstype.print = Szöveget rajzol a kiírási pufferből.\nTörli a kiírás puffert. +graphicstype.linepoly = Szabályos sokszög körvonalának rajzolása. +graphicstype.triangle = Egy háromszög kitöltése. +graphicstype.image = Kép rajzolása valamilyen tartalomról.\nPéldául: [accent]@router[] vagy [accent]@dagger[]. +graphicstype.print = Szöveget rajzol a kiírási pufferből.\nCsak ASCII karakterek használhatók.\nTörli a kiírás puffert. lenum.always = Mindig igaz. -lenum.idiv = Egész számok osztása. -lenum.div = Osztás.\nVisszatér [accent]null[]-val a nullával való osztásnál. -lenum.mod = Moduló. -lenum.equal = Egyenlő. Kényszeríti a típusokat.\nA nem nulla értékű objektumok értéke 1 lesz, egyébként 0. +lenum.idiv = Egész osztás. +lenum.div = Osztás.\nNullával való osztáskor a visszatérési érték [accent]null[]. +lenum.mod = Modulo. +lenum.equal = Egyenlő. Kényszeríti a típusokat.\nA nem null értékű objektumok értéke 1 lesz, egyébként 0. lenum.notequal = Nem egyenlő. Kényszeríti a típusokat. -lenum.strictequal = Szigorúan egyenlő. Nem kényszeríti a típusokat.\nA(z) [accent]null[] ellenőrzésére használható. +lenum.strictequal = Szigorúan egyenlőség. Nem kényszeríti a típusokat.\nA [accent]null[] ellenőrzésére is használható. lenum.shl = Biteltolás balra. lenum.shr = Biteltolás jobbra. lenum.or = Bitenkénti VAGY. lenum.land = Logikai ÉS. lenum.and = Bitenkénti ÉS. lenum.not = Bitenkénti átfordítás. -lenum.xor = Bitenkénti KIZÁRÓ-VAGY. +lenum.xor = Bitenkénti KIZÁRÓ VAGY. -lenum.min = Legalább két szám. -lenum.max = Legfeljebb két szám. +lenum.min = Két szám minimuma. +lenum.max = Két szám maximuma. lenum.angle = A vektor szöge fokban. lenum.anglediff = Két szög abszolút távolsága fokban. lenum.len = A vektor hossza. @@ -2446,15 +2446,15 @@ lenum.sin = Szinusz, fokban. lenum.cos = Koszinusz, fokban. lenum.tan = Tangens, fokban. -lenum.asin = ARC-szinusz, fokban. -lenum.acos = ARC-koszinusz, fokban. -lenum.atan = ARC-tangens, fokban. +lenum.asin = Arkusz szinusz, fokban. +lenum.acos = Arkusz koszinusz, fokban. +lenum.atan = Arkusz tangens, fokban. #not a typo, look up 'range notation' -lenum.rand = Véletlen decimális szám a [0, érték] tartományban. +lenum.rand = Véletlenszerű decimális szám a [0, érték) tartományban. lenum.log = Természetes logaritmus (ln). lenum.log10 = 10-es alapú logaritmus. -lenum.noise = 2D szimplex zaj. +lenum.noise = 2D-s szimplex zaj. lenum.abs = Abszolút érték. lenum.sqrt = Négyzetgyök. @@ -2469,70 +2469,70 @@ lenum.player = Egy játékos által irányított egység. lenum.ore = Érclelőhely. lenum.damaged = Sérült szövetséges épület. -lenum.spawn = Ellenséges kezdőpont.\nLehet egy Támaszpont vagy egy pozíció. +lenum.spawn = Ellenséges kezdőpont.\nLehet támaszpont vagy pozíció. lenum.building = Épület egy adott csoportban. -lenum.core = Bármilyen Támaszpont. -lenum.storage = Raktárépület, pl. Vault. +lenum.core = Bármilyen támaszpont. +lenum.storage = Raktárépület, például tároló. lenum.generator = Energiát termelő épületek. lenum.factory = Nyersanyagokat feldolgozó épületek. lenum.repair = Javítási pontok. lenum.battery = Bármilyen akkumulátor. -lenum.resupply = Utánpótlási pontok.\nCsak akkor van jelentősége, ha az [accent]"Egység Lőszer"[] engedélyezve van. -lenum.reactor = Ütközéses/Tórium Erőmű. +lenum.resupply = Utánpótlási pontok.\nCsak akkor van jelentősége, ha az [accent]„egység lőszere”[] engedélyezve van. +lenum.reactor = Ütközéses- vagy tóriumerőmű. lenum.turret = Bármilyen lövegtorony. -sensor.in = Az épület/egység, amelyet érzékelni kell. +sensor.in = Az érzékelendő épület/egység. -radar.from = Épület, ahonnan érzékelni kell.\nAz érzékelő hatótávolságát az épület hatótávolsága korlátozza. -radar.target = Szűrő az érzékelhető egységekhez. +radar.from = Az épület, ahonnan érzékelni kell.\nAz érzékelő hatótávolságát az épület hatótávolsága korlátozza. +radar.target = Szűrő az érzékelendő egységekhez. radar.and = További szűrők. radar.order = Rendezési sorrend. 0-tól visszafelé. radar.sort = Az eredmények rendezésének metrikája. radar.output = Változó, amelybe a kimeneti egységet írja. -unitradar.target = Szűrő az érzékelhető egységekhez. +unitradar.target = Érzékelendő egységek szűrése. unitradar.and = További szűrők. unitradar.order = Rendezési sorrend. 0-tól visszafelé. unitradar.sort = Az eredmények rendezésének metrikája. unitradar.output = Változó, amelybe a kimeneti egységet írja. -control.of = Épület az irányításhoz. +control.of = Irányítandó épület. control.unit = Megcélozandó egység/épület. -control.shoot = Akár lőni is lehet. +control.shoot = Lőjön-e. -unitlocate.enemy = Akár az ellenséges épületek felderítése. -unitlocate.found = Függetlenül attól, hogy a tárgy meglett-e találva. +unitlocate.enemy = Felderítse-e az ellenséges épületeket. +unitlocate.found = Megtalálta-e az objektumot. unitlocate.building = Kimeneti változó a megtalált épülethez. -unitlocate.outx = Kimeneti X koordináta. -unitlocate.outy = Kimeneti Y koordináta. +unitlocate.outx = Kimenet X koordinátája. +unitlocate.outy = Kimenet Y koordinátája. unitlocate.group = Keresendő épületcsoport. lenum.idle = Ne mozdulj, de folytasd az építkezést/bányászatot.\nAz alapértelmezett állapot. -lenum.stop = Mozgás/bányászás/építés leállítása. -lenum.unbind = A logikai vezérlés teljes kikapcsolása.\nSzokásos mesterséges intelligencia visszaállítása. +lenum.stop = Mozgás/bányászat/építés leállítása. +lenum.unbind = A logikai vezérlés teljes kikapcsolása.\nSzokásos mesterséges intelligencia folytatása. lenum.move = Mozgás a pontos pozícióba. -lenum.approach = Közelítsen meg egy pozíciót egy sugárral. -lenum.pathfind = Útkeresés az ellenséges kezdőponthoz. -lenum.autopathfind = Automatikus útkeresés a legközelebbi ellenséges Támaszponthoz, vagy ledobási ponthoz.\nEz ugyanaz, mint a normál hullámos ellenséges útkeresés. -lenum.target = Lőj egy helyet. -lenum.targetp = Lőj egy célpontra sebesség-előrejelzéssel. -lenum.itemdrop = Dobj le egy nyersanyagot. -lenum.itemtake = Vegyél fel egy nyersanyagot egy épületből. -lenum.paydrop = Dobd le az aktuális rakományt. -lenum.paytake = A rakomány felvétele az aktuális helyen. -lenum.payenter = Lépj be/szállj le arra a rakomány blokkra, amelyen az egység van. +lenum.approach = Egy pozíció megközelítése egy sugárral. +lenum.pathfind = Útkeresés a megadott pozícióhoz. +lenum.autopathfind = Automatikus útkeresés a legközelebbi ellenséges támaszponthoz vagy ledobási ponthoz.\nEz ugyanaz, mint a normál hullámos ellenséges útkeresés. +lenum.target = Lövés egy pozícióra. +lenum.targetp = Lövés egy célpontra sebesség-előrejelzéssel. +lenum.itemdrop = Egy nyersanyag ledobása. +lenum.itemtake = Egy nyersanyag felvétele egy épületből. +lenum.paydrop = A jelenlegi rakomány ledobása. +lenum.paytake = Rakomány felvétele a jelenlegi helyen. +lenum.payenter = Belépés/leszállás a rakományblokkra, amelyen az egység van. lenum.flag = Számjegyes egységjelölő. -lenum.mine = Bánya egy helyen. -lenum.build = Építs egy szerkezetet. -lenum.getblock = Egy épület és a koordinátái típusának lekérdezése. \nAz egységnek a pozíciótartományon belül kell lennie.\nA nem szilárd épületek [accent]@solid[] típusúak lesznek. -lenum.within = Ellenőrizze, hogy az egység egy pozíció közelében van-e. -lenum.boost = Erősítés indítás/leállítás. +lenum.mine = Bányászat egy helyen. +lenum.build = Egy épület építése. +lenum.getblock = Az épület, talaj és típus lekérdezése a koordinátákon.\nAz egységnek a pozíciótartományon belül kell lennie.\nA szilárd dolgok, melyek nem épületek, [accent]@solid[] típusúak lesznek. +lenum.within = Ellenőrzés, hogy az egység egy pozíció közelében van-e. +lenum.boost = Erősítés indítása/leállítása. -lenum.flushtext = A nyomtatási puffer tartalmának ürítése a jelölőre, ha alkalmazható.\nHa a fetch értéke igaz, megpróbálja lehívni a tulajdonságokat a pálya Helyi Csomagjából, vagy a játék csomagjából. -lenum.texture = A textúra neve közvetlenül a játék textúra atlaszából (kebab-case elnevezési stílus használatával).\nHa a printFlush értéke igaz, akkor a szöveges puffer tartalmát használja szöveges argumentumként. +lenum.flushtext = Az írási puffer tartalmának ürítése a jelölőre, ha alkalmazható.\nHa a „fetch” igaz, akkor megpróbálja lekérni a tulajdonságokat a pálya nyelvi csomagjából vagy a játék csomagjából. +lenum.texture = A textúra neve közvetlenül a játék textúraatlaszából (ún. kebab-case elnevezési stílus használatával).\nHa a „printFlush” igaz, akkor a szöveges puffer tartalmát használja szöveges argumentumként. lenum.texturesize = A textúra mérete csempékben. A nulla érték a jelölő szélességét az eredeti textúra méretére skálázza. -lenum.autoscale = A jelölő skálázása a játékos nagyítási szintjének megfelelően. -lenum.posi = Indexelt pozíció, vonal- és négyszögjelzőkhöz használatos, ahol a nulla index az első pozíció. -lenum.uvi = A textúra pozíciója nullától egyig, négyzet jelölőkhöz használatos. -lenum.colori = Indexelt szín, vonal- és négyzet jelölőkhöz használatos, ahol a nulla index az első szín. +lenum.autoscale = Skálázva legyen-e a jelölő a játékos nagyítási szintjének megfelelően. +lenum.posi = Indexelt pozíció, vonal- és négyszögjelzőkhöz használatos, ahol a nulla az első pozíció. +lenum.uvi = A textúra pozíciója nullától egyig, négyzetjelölőkhöz használatos. +lenum.colori = Indexelt szín, vonal- és négyzetjelölőkhöz használatos, ahol a nulla az első szín. From 08cb83b8e297181972590139d1d346ce36fe17e2 Mon Sep 17 00:00:00 2001 From: TheRadioactiveBanana <89061718+TheRadioactiveBanana@users.noreply.github.com> Date: Sat, 30 Mar 2024 00:32:46 +0530 Subject: [PATCH 045/348] UK nodes switching internet for higher stability and lower ping (#9682) Also probably moving TD, RA and some others to UK --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 8553f2187f..d62c916a04 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -146,7 +146,7 @@ }, { "name": "Eradication Mindustry", - "address": ["140.238.246.78:7000", "140.238.246.78:7001", "140.238.246.78:7002", "140.238.246.78:7003", "140.238.246.78:7004", "130.61.22.183:7000", "130.61.22.183:7001", "130.61.22.183:7002", "130.61.22.183:7003", "130.61.22.183:7004", "130.61.22.183:7005", "130.61.220.99:7000", "130.61.220.99:7001", "130.61.220.99:7002", "130.61.220.99:7003", "130.61.220.99:7004", "130.61.220.99:7005", "77.100.100.249:7000", "77.100.100.249:7001"] + "address": ["140.238.246.78:7000", "140.238.246.78:7001", "140.238.246.78:7002", "140.238.246.78:7003", "130.61.22.183:7000", "130.61.22.183:7001", "130.61.22.183:7002", "130.61.22.183:7003", "130.61.22.183:7004", "130.61.22.183:7005", "130.61.220.99:7000", "130.61.220.99:7001", "130.61.220.99:7002", "130.61.220.99:7003", "130.61.220.99:7004", "130.61.220.99:7005", "62.30.47.116:7000", "62.30.47.116:7001", "62.30.47.116:7002"] }, { "name": "Conservatory", From fe10d92e05fccb2a0337da64275f7fe9b4057f70 Mon Sep 17 00:00:00 2001 From: RedsonBr140 Date: Fri, 29 Mar 2024 16:07:48 -0300 Subject: [PATCH 046/348] feat: update the pt_BR translation (#8219) * feat: Translate mods and campaign. * feat: translate global and world-specific hints. --------- Co-authored-by: Anuken --- core/assets/bundles/bundle_pt_BR.properties | 123 ++++++++++---------- 1 file changed, 59 insertions(+), 64 deletions(-) diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index 8dcf3382bb..5d055120e0 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -41,16 +41,16 @@ be.ignore = Ignorar be.noupdates = Nenhuma atualização encontrada. be.check = Checar por atualizações -mods.browser = Mod Browser +mods.browser = Navegador de mods mods.browser.selected = Mod selecionado mods.browser.add = Instalar mods.browser.reinstall = Reinstalar -mods.browser.view-releases = View Releases -mods.browser.noreleases = [scarlet]Nenhum lançamento encontrado\n[accent]Não foi possível encontrar nenhum lançamento para este mod. Verifique se o repositório do mod tem algum lançamento publicado. -mods.browser.latest = -mods.browser.releases = Releases +mods.browser.view-releases = Ver versões +mods.browser.noreleases = [scarlet]Nenhuma versão encontrada\n[accent]Não foi possível encontrar nenhuma versão do mod. Veja se o repositório do mod possui alguma versão publicada. +mods.browser.latest = +mods.browser.releases = Versões mods.github.open = Repositório -mods.github.open-release = Release Page +mods.github.open-release = Página da versão mods.browser.sortdate = Ordenar por mais recente mods.browser.sortstars = Ordenar por estrelas @@ -146,9 +146,9 @@ mod.multiplayer.compatible = [gray]Compatível com Multiplayer mod.disable = Desati-\nvar mod.content = Conteúdo: mod.delete.error = Incapaz de deletar o mod. O arquivo talvez esteja em uso. -mod.incompatiblegame = [red]Outdated Game -mod.incompatiblemod = [red]Incompatible -mod.blacklisted = [red]Unsupported +mod.incompatiblegame = [red]Jogo desatualizado +mod.incompatiblemod = [red]Incompatível +mod.blacklisted = [red]Não suportado mod.unmetdependencies = [red]Unmet Dependencies mod.erroredcontent = [scarlet]Erros no conteúdo mod.circulardependencies = [red]Circular Dependencies @@ -190,9 +190,9 @@ unlocked = Novo bloco desbloqueado! available = Nova pesquisa disponível! unlock.incampaign = < Desbloqueie na campanha para mais detalhes > campaign.select = Selecione a campanha inicial -campaign.none = [lightgray]Selecione um planeta para começar.\nEle pode ser alterado a qualquer momento. -campaign.erekir = Conteúdo mais novo e mais polido. Progressão de campanha principalmente linear.\n\nMapas de maior qualidade e experiência geral. -campaign.serpulo = Conteúdo mais antigo; a experiência clássica. Mais aberto.\n\nMapas e mecânicas de campanha potencialmente desbalanceados. Menos polido. +campaign.none = [lightgray]Selecione um planeta para começar nele.\nVocê pode mudar de planeta a qualquer momento. +campaign.erekir = Novo, conteúdo mais polido. Uma progressão mais linear na campanha.\n\nExperiência geral e mapas de maior qualidade. +campaign.serpulo = Conteúdo antigo; a experiência clássica. Mais aberto.\n\nMapas e mecânicas de campanha potencialmente desbalanceados. Menos polido. completed = [accent]Completado techtree = Árvore Tecnológica techtree.select = Seleção de Árvore Tecnológica @@ -383,8 +383,8 @@ pausebuilding = [accent][[{0}][] para parar a construção resumebuilding = [scarlet][[{0}][] para continuar a construção enablebuilding = [scarlet][[{0}][] para habilitar construção showui = Interface escondida.\nPressione [accent][[{0}][] para mostrar a interface. -commandmode.name = [accent]Command Mode -commandmode.nounits = [no units] +commandmode.name = [accent]Modo de comando +commandmode.nounits = [nenhuma unidade] wave = [accent]Horda {0} wave.cap = [accent]Horda {0}/{1} wave.waiting = Proxima horda em {0} @@ -1851,13 +1851,12 @@ hint.research = Use o botão de \ue875 [accent]Pesquisa[] para pesquisar novas t hint.research.mobile = Use o botão de \ue875 [accent]Pesquisa[] no \ue88c [accent]Menu[] para pesquisar novas tecnologias. hint.unitControl = Segure [accent][[L-ctrl][] e [accent]click[] para controlar suas unidades ou torretas. hint.unitControl.mobile = [accent][[Toque duas vezes][] para controlar suas unidades ou torretas. -hint.unitSelectControl = To control units, enter [accent]command mode[] by holding [accent]L-shift.[]\nWhile in command mode, click and drag to select units. [accent]Right-click[] a location or target to command units there. -hint.unitSelectControl.mobile = To control units, enter [accent]command mode[] by pressing the [accent]command[] button in the bottom left.\nWhile in command mode, long-press and drag to select units. Tap a location or target to command units there. +hint.unitSelectControl = Para controlar unidades, entre no [accent]modo de comando[] segurando [accent]Shift esquerdo.[]\nEnquanto no modo de comando, clique e segure pra selecionar unidades. Clique com o [accent]Botão direito[] em um lugar ou alvo para mandar as unidades para lá. +hint.unitSelectControl.mobile = Para controlar unidades, entre no [accent]modo de comando[] segurando o botão de [accent]comando[] no canto inferior esquerdo.\nEnquanto no modo de comando, segure e arraste pra selecionar unidades. Toque em algum lugar ou alvo para mandar as unidades para lá. hint.launch = Quando recursos suficientes forem coletados, você pode [accent]Lançar[] selecionando setores próximos a partir do \ue827 [accent]Mapa[] no canto inferior direito. hint.launch.mobile = Quando recursos suficientes forem coletados, você pode [accent]Lançar[] selecionando setores próximos a partir do \ue827 [accent]Mapa[] no \ue88c [accent]Menu[]. hint.schematicSelect = Segure [accent][[F][] e arraste para selecionar blocos para copiar e colar.\n\n[accent][[Middle Click][] para copiar um bloco só. -hint.rebuildSelect = Hold [accent][[B][] and drag to select destroyed block plans.\nThis will rebuild them automatically. -hint.rebuildSelect.mobile = Select the \ue874 copy button, then tap the \ue80f rebuild button and drag to select destroyed block plans.\nThis will rebuild them automatically. +hint.rebuildSelect = Segure [accent][[B][] e arraste para selecionar blocos destruídos.\nIsso irá reconstruí-los automaticamente. hint.conveyorPathfind = Segure [accent][[L-Ctrl][] enquanto arrasta as esteiras para gerar automaticamente um caminho. hint.conveyorPathfind.mobile = Ative o \ue844 [accent]modo diagonal[] e arraste as esteiras para gerar automaticamente um caminho. hint.boost = Segure [accent][[L-Shift][] para voar sobre obstáculos com a sua unidade.\n\nApenas algumas unidades terrestres tem propulsores. @@ -1874,53 +1873,49 @@ hint.presetDifficulty = Esse setor tem um [scarlet]alto nível de ameaça inimig hint.coreIncinerate = Depois que o núcleo ter recebido até a capacidade máxima de um item, qualquer item do mesmo tipo que ele receber será [accent]incinerado[]. hint.factoryControl = Para definir a [accent]o local de saída[] de uma fábrica de unidades, clique em uma fábrica enquanto estiver no modo de comando, depois clique com o botão direito em um local.\nAs unidades produzidas por ela se moverão automaticamente para lá. hint.factoryControl.mobile = Para definir a [accent]o local de saída[] de uma fábrica de unidades, toque em uma fábrica enquanto estiver no modo de comando, depois toque em um local.\nAs unidades produzidas por ela se moverão automaticamente para lá. -gz.mine = Move near the \uf8c4 [accent]copper ore[] on the ground and click to begin mining. -gz.mine.mobile = Move near the \uf8c4 [accent]copper ore[] on the ground and tap it to begin mining. -gz.research = Open the \ue875 tech tree.\nResearch the \uf870 [accent]Mechanical Drill[], then select it from the menu in the bottom right.\nClick on a copper patch to place it. -gz.research.mobile = Open the \ue875 tech tree.\nResearch the \uf870 [accent]Mechanical Drill[], then select it from the menu in the bottom right.\nTap on a copper patch to place it.\n\nPress the \ue800 [accent]checkmark[] at the bottom right to confirm. -gz.conveyors = Research and place \uf896 [accent]conveyors[] to move the mined resources\nfrom drills to the core.\n\nClick and drag to place multiple conveyors.\n[accent]Scroll[] to rotate. -gz.conveyors.mobile = Research and place \uf896 [accent]conveyors[] to move the mined resources\nfrom drills to the core.\n\nHold down your finger for a second and drag to place multiple conveyors. -gz.drills = Expand the mining operation.\nPlace more Mechanical Drills.\nMine 100 copper. -gz.lead = \uf837 [accent]Lead[] is another commonly used resource.\nSet up drills to mine lead. -gz.moveup = \ue804 Move up for further objectives. -gz.turrets = Research and place 2 \uf861 [accent]Duo[] turrets to defend the core.\nDuo turrets require \uf838 [accent]ammo[] from conveyors. -gz.duoammo = Supply the Duo turrets with [accent]copper[], using conveyors. -gz.walls = [accent]Walls[] can prevent oncoming damage from reaching buildings.\nPlace \uf8ae [accent]copper walls[] around the turrets. -gz.defend = Enemy incoming, prepare to defend. -gz.aa = Flying units cannot easily be dispatched with standard turrets.\n\uf860 [accent]Scatter[] turrets provide excellent anti-air, but require \uf837 [accent]lead[] as ammo. -gz.scatterammo = Supply the Scatter turret with [accent]lead[], using conveyors. -gz.supplyturret = [accent]Supply Turret -gz.zone1 = This is the enemy drop zone. -gz.zone2 = Anything built in the radius is destroyed when a wave starts. -gz.zone3 = A wave will begin now.\nGet ready. -gz.finish = Build more turrets, mine more resources,\nand defend against all the waves to [accent]capture the sector[]. -onset.mine = Click to mine \uf748 [accent]beryllium[] from walls.\n\nUse [accent][[WASD] to move. -onset.mine.mobile = Tap to mine \uf748 [accent]beryllium[] from walls. -onset.research = Open the \ue875 tech tree.\nResearch, then place a \uf73e [accent]turbine condenser[] on the vent.\nThis will generate [accent]power[]. -onset.bore = Research and place a \uf741 [accent]plasma bore[].\nThis automatically mines resources from walls. -onset.power = To [accent]power[] the plasma bore, research and place a \uf73d [accent]beam node[].\nConnect the turbine condenser to the plasma bore. -onset.ducts = Research and place \uf799 [accent]ducts[] to move the mined resources from the plasma bore to the core.\nClick and drag to place multiple ducts.\n[accent]Scroll[] to rotate. -onset.ducts.mobile = Research and place \uf799 [accent]ducts[] to move the mined resources from the plasma bore to the core.\n\nHold down your finger for a second and drag to place multiple ducts. -onset.moremine = Expand the mining operation.\nPlace more Plasma Bores and use beam nodes and ducts to support them.\nMine 200 beryllium. -onset.graphite = More complex blocks require \uf835 [accent]graphite[].\nSet up plasma bores to mine graphite. -onset.research2 = Begin researching [accent]factories[].\nResearch the \uf74d [accent]cliff crusher[] and \uf779 [accent]silicon arc furnace[]. -onset.arcfurnace = The arc furnace needs \uf834 [accent]sand[] and \uf835 [accent]graphite[] to create \uf82f [accent]silicon[].\n[accent]Power[] is also required. -onset.crusher = Use \uf74d [accent]cliff crushers[] to mine sand. -onset.fabricator = Use [accent]units[] to explore the map, defend buildings, and attack the enemy. Research and place a \uf6a2 [accent]tank fabricator[]. -onset.makeunit = Produce a unit.\nUse the "?" button to see selected factory requirements. -onset.turrets = Units are effective, but [accent]turrets[] provide better defensive capabilities if used effectively.\nPlace a \uf6eb [accent]Breach[] turret.\nTurrets require \uf748 [accent]ammo[]. -onset.turretammo = Supply the turret with [accent]beryllium ammo.[] -onset.walls = [accent]Walls[] can prevent oncoming damage from reaching buildings.\nPlace some \uf6ee [accent]beryllium walls[] around the turret. -onset.enemies = Enemy incoming, prepare to defend. -onset.defenses = [accent]Set up defenses:[lightgray] {0} -onset.attack = The enemy is vulnerable. Counter-attack. -onset.cores = New cores can be placed on [accent]core tiles[].\nNew cores function as forward bases and share a resource inventory with other cores.\nPlace a \uf725 core. -onset.detect = The enemy will be able to detect you in 2 minutes.\nSet up defenses, mining, and production. +gz.mine = Vá para perto do \uf8c4 [accent]minério de cobre[] no chão e clique para começar a minerar. +gz.mine.mobile = Vá para perto do \uf8c4 [accent]minério de cobre[] no chão e toque nele para começar a minerar. +gz.research = Abra a \ue875 árvore tecnológica.\nPesquise a \uf870 [accent]Broca mecânica[], Depois selecione-a pelo menu no canto inferior direito.\nClique no cobre para coloca-la. +gz.research.mobile = Abra a \ue875 árvore tecnológica.\nPesquise a \uf870 [accent]Broca mecânica[], Depois selecione-a pelo menu no canto inferior direito.\nClique no cobre para colocá-la.\n\nPressione a \ue800 [accent]confirmação[] no canto inferior direito para confirmar. +gz.conveyors = Pesquise e coloque \uf896 [accent]esteiras[] para mover os recursos minerados\ndas brocas para o núcleo.\n\nClique e arraste para pôr multiplas esteiras.\n[accent]Scroll[] para rotacionar. +gz.conveyors.mobile = Pesquise e coloque \uf896 [accent]esteiras[] para mover os recursos minerados\ndas brocas para o núcleo.\n\nSegure por um segundo e arraste para colocar múltiplas esteiras. +gz.drills = Expanda a mineração.\nColoque mais Brocas Mecânicas.\nMinere 100 cobres. +gz.lead = \uf837 [accent]Chumbo[] é outro recurso comumente usado.\nColoque brocas para minerar chumbo. +gz.moveup = \ue804 Vá para cima para outros objetivos. +gz.turrets = Pesquise e coloque 2 torretas \uf861 [accent]Duo[] para defender o núcleo.\ntorretas Duo requerem \uf838 [accent]munição[] de esteiras. +gz.duoammo = Abasteça as torretas Duo com [accent]cobre[], usando esteiras. +gz.walls = [accent]Muros[] podem previnir danos recebidos de atingir as construções.\nColoque \uf8ae [accent]muros de cobre[] em volta das torretas. +gz.defend = Inimigos vindo, prepare-se para defender. +gz.aa = Unidades flutuantes não podem ser destruidas facilmente por torretas comuns.\nTorretas\uf860 [accent]Scatter[] Proveem ótima defesa aérea, mas requerem \uf837 [accent]chumbo[] como munição. +gz.scatterammo = Abasteça a torreta Scatter com [accent]chumbo[], usando esteiras. +gz.supplyturret = [accent]Abasteça a torreta +gz.zone1 = Essa é a zona de spawn inimigo. +gz.zone2 = Qualquer coisa construida nesta área será destruida quando uma horda começar. +gz.zone3 = Uma horda vai começar agora\nSe prepare. +gz.finish = Construa mais torretas, minere mais recursos,\ne se defenda de todas as hordas para [accent]capturar o setor[]. +onset.mine = Clique para minerar \uf748 [accent]berílio[] das paredes.\n\nUse [accent][[WASD] para se mover. +onset.mine.mobile = Toque para minerar \uf748 [accent]berílio[] das paredes. +onset.research = Abra a \ue875 árvore tecnológica.\nPesquise, e então coloque um \uf73e [accent]Condensador de Turbina[] na ventilação.\nIsso vai gerar [accent]energia[]. +onset.bore = Pesquise e coloque uma \uf741 [accent]Mineradora de Plasma[].\nEla minera recursos das paredes automaticamente. +onset.power = Para[accent]alimentar[] a Mineradora de Plasma, pesquise e coloque uma \uf73d [accent]Célula de Feixe[].\nConecte o condensador de turbina ao minerador de plasma. +onset.ducts = Pesquise e coloque \uf799 [accent]dutos[] para mover recursos minerados da mineradora de plasma para o núcleo.\nClique e segure para colocar múltiplos dutos.\n[accent]Scroll[] para rotacionar. +onset.ducts.mobile = Pesquise e coloque \uf799 [accent]dutos[] para mover recursos minerados da mineradora de plasma para o núcleo.\n\nSegure por um segundo e arraste para colocar múltiplos dutos. +onset.moremine = Expanda a mineração.\nColoque mais Mineradoras de Plasma, use as Células de Feixe e dutos para isso.\nMinere 200 berílios. +onset.graphite = Blocos mais complexos requerem \uf835 [accent]grafite[].\nColoque Mineradoras de Plasma para minerar grafite. +onset.research2 = Comece a pesquisar [accent]fábricas[].\nPesquise o \uf74d [accent]Esmagador de Penhasco[] e o \uf779 [accent]silicon arc furnace[]. +onset.arcfurnace = O arc furnace precisa de \uf834 [accent]areia[] e \uf835 [accent]grafite[] para criar \uf82f [accent]silício[].\n[accent]Energia[] também é necessária. +onset.crusher = Use o \uf74d [accent]Esmagador de Areia[] para minerar areia. +onset.fabricator = Use [accent]unidades[] para explorar o mapa, defender construções e atacar o inimigo. Pesquise e coloque um \uf6a2 [accent]Fabricador de Tanques[]. +onset.makeunit = Produza uma unidade.\nUse o botão "?" para ver os requisitos da fábrica selecionada. +onset.turrets = Unidades são efetivas, mas [accent]torretas[] proveem melhores capacidades defensivas se usadas efetivamente.\nColoque uma torreta \uf6eb [accent]Breach[].\nTorretas requerem \uf748 [accent]munição[]. +onset.turretammo = Abasteça a torreta com [accent]munição de berílio.[] +onset.walls = [accent]Muros[] podem previnir danos recebidos de atingir as construções.\nColoque \uf6ee [accent]muros de berílio[] em volta das torretas. + +onset.enemies = Inimigo vindo, se prepare. +onset.attack = O inimigo está vulnerável. Contra ataque. +onset.cores = Novos núcleos podem ser colocados em [accent]ladrilhos de núcleo[].\nNovos núcleos funcionam como bases avançadas e compartilham seus recursos com outros núcleos.\nColoque um \uf725 núcleo. +onset.detect = O inimigo poderá te detectar em 2 minutos.\nConstrua defesas, mineração e produção. -#Don't translate these yet! -onset.commandmode = Hold [accent]shift[] to enter [accent]command mode[].\n[accent]Left-click and drag[] to select units.\n[accent]Right-click[] to order selected units to move or attack. -onset.commandmode.mobile = Press the [accent]command button[] to enter [accent]command mode[].\nHold down a finger, then [accent]drag[] to select units.\n[accent]Tap[] to order selected units to move or attack. -aegis.tungsten = Tungsten can be mined using an [accent]impact drill[].\nThis structure requires [accent]water[] and [accent]power[]. split.pickup = Some blocks can be picked up by the core unit.\nPick up this [accent]container[] and place it onto the [accent]payload loader[].\n(Default keys are [ and ] to pick up and drop) split.pickup.mobile = Some blocks can be picked up by the core unit.\nPick up this [accent]container[] and place it onto the [accent]payload loader[].\n(To pick up or drop something, long-press it.) split.acquire = You must acquire some tungsten to build units. From 520c423a597328f0151ccc1b2ec3790d36341c54 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Fri, 29 Mar 2024 19:08:39 +0000 Subject: [PATCH 047/348] Automatic bundle update --- core/assets/bundles/bundle_hu.properties | 86 ++++++++++----------- core/assets/bundles/bundle_pt_BR.properties | 5 ++ 2 files changed, 48 insertions(+), 43 deletions(-) diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index 3a372d6433..88ac610b4f 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -743,7 +743,7 @@ weather.sandstorm.name = Homokvihar weather.sporestorm.name = Spóravihar weather.fog.name = Köd -campaign.playtime =  [lightgray]Játékidő a szektorban: {0} +campaign.playtime = \uf129 [lightgray]Játékidő a szektorban: {0} campaign.complete = [accent]Gratulálunk.\n\nAz ellenség legyőzve a(z) {0} bolygón.\n[lightgray]Az utolsó szektor meghódítása megtörtént. sectorlist = Szektorok @@ -1859,79 +1859,79 @@ hint.respawn = Ahhoz, hogy drónként újraéledj, nyomd meg a [accent][[V][] go hint.respawn.mobile = Átvetted az irányítást egy egység vagy épület felett. Ahhoz, hogy drónként újraéledj, [accent]koppints a profilképre a bal felső sarokban.[] hint.desktopPause = Nyomd meg a [accent][[Szóközt][] a játék szüneteltetéséhez vagy folytatásához. hint.breaking = [accent]Jobb egérgombbal[] és húzással lebonthatod a blokkokat. -hint.breaking.mobile = Használd a jobb alsó sarokban lévő  [accent]kalapács[] gombot a blokkok törléséhez.\n\nTartsd lenyomva az ujjad és húzd, hogy nagyobb területet tudj kijelölni. +hint.breaking.mobile = Használd a jobb alsó sarokban lévő \ue817 [accent]kalapács[] gombot a blokkok törléséhez.\n\nTartsd lenyomva az ujjad és húzd, hogy nagyobb területet tudj kijelölni. hint.blockInfo = Egy blokk információinak megtekintéséhez válaszd ki az épületet az [accent]építési menüben[], majd válaszd a [accent][[?][] gomb jobb oldalt. hint.derelict = Az [accent]elhagyatott[] szerkezetek régi bázisok maradványai, amelyek már nem működnek.\n\nEzeket az épületeket le lehet [accent]bontani[] nyersanyagokért, vagy meg is lehet javítani őket. -hint.research = Használd a  [accent]Fejlesztési fa[] gombot, hogy új technológiákat fedezz fel. -hint.research.mobile = Használd a  [accent]Fejlesztési fa[] gombot a  [accent]menüben[], hogy új technológiákat fedezz fel. +hint.research = Használd a \ue875 [accent]Fejlesztési fa[] gombot, hogy új technológiákat fedezz fel. +hint.research.mobile = Használd a \ue875 [accent]Fejlesztési fa[] gombot a \ue88c [accent]menüben[], hogy új technológiákat fedezz fel. hint.unitControl = Nyomd le a [accent][[bal Ctrl][] gombot, és kattints [accent]jobb egérgombbal[] a baráti egység vagy lövegtorony irányításához. hint.unitControl.mobile = [accent][[Dupla koppintással][] irányíthatók kézileg a szövetséges egységek vagy lövegtornyok. hint.unitSelectControl = Az egységek irányításához lépj be [accent]parancs módba[] a [accent]bal Shift[] lenyomva tartásával.\nParancs módban az egységek kijelöléséhez kattints, és húzd az egeret. A [accent]jobb egérgombbal[] küldd az egységeket a helyszínre vagy a célponthoz. hint.unitSelectControl.mobile = Az egységek irányításához lépj be [accent]parancs módba[] a bal alsó sarokban lévő [accent]parancs[] gombbal.\nParancs módban az egységek kiválasztásához érintsd meg a kijelzőt és húzással jelöld ki az egységeket. Koppintással küldd az egységeket a helyszínre vagy a célponthoz. -hint.launch = Ha elegendő nyersanyagot gyűjtöttél össze, akkor [accent]lődd ki[] a támaszpontot a következő szektorba, úgy, hogy megnyitod a  [accent]Bolygótérképet[] a jobb alsó sarokban, és átforgatod az új helyszínre. -hint.launch.mobile = Ha elegendő nyersanyagot gyűjtöttél össze, akkor [accent]lődd ki[] el a támaszpontot egy közeli szektorba, úgy, hogy kiválasztasz egy szektort a  [accent]Menüben[] a  [accent]Bolygótérképről[]. +hint.launch = Ha elegendő nyersanyagot gyűjtöttél össze, akkor [accent]lődd ki[] a támaszpontot a következő szektorba, úgy, hogy megnyitod a \ue827 [accent]Bolygótérképet[] a jobb alsó sarokban, és átforgatod az új helyszínre. +hint.launch.mobile = Ha elegendő nyersanyagot gyűjtöttél össze, akkor [accent]lődd ki[] el a támaszpontot egy közeli szektorba, úgy, hogy kiválasztasz egy szektort a \ue88c [accent]Menüben[] a \ue827 [accent]Bolygótérképről[]. hint.schematicSelect = Tartsd nyomja az [accent][[F][] gombot több épület kijelöléséhez és másolásához.\n\n[accent][[Középső kattintással][] egy adott blokktípus másolható. hint.rebuildSelect = Tartsd nyomva a [accent][[B][] gombot és húzással jelöld ki a megsemmisített blokkterveket.\nEz automatikusan újraépíti őket. -hint.rebuildSelect.mobile = Válaszd a  másolás gombot, majd koppints az  újjáépítés gombra, és húzd a megsemmisült blokktervek kijelöléséhez.\nEz automatikusan újraépíti őket. +hint.rebuildSelect.mobile = Válaszd a \ue874 másolás gombot, majd koppints az \ue80f újjáépítés gombra, és húzd a megsemmisült blokktervek kijelöléséhez.\nEz automatikusan újraépíti őket. hint.conveyorPathfind = Tartsd nyomva a [accent][[bal Ctrl][] gombot a szállítószalagok lerakása közben, hogy a játék útvonalat állítson elő. -hint.conveyorPathfind.mobile = Engedélyezd az  [accent]átlós módot[], és tegyél le egyszerre több szállítószalagot, hogy a játék útvonalat állítson elő. +hint.conveyorPathfind.mobile = Engedélyezd az \ue844 [accent]átlós módot[], és tegyél le egyszerre több szállítószalagot, hogy a játék útvonalat állítson elő. hint.boost = Tartsd nyomva a [accent][[bal Shift][] gombot, hogy átrepülj az akadályok felett.\n\nErre csak néhány földi egység képes. hint.payloadPickup = Nyomd meg a [accent][[[] gombot a kis blokkok vagy egységek felemeléséhez. hint.payloadPickup.mobile = [accent]Koppints és tartsd lenyomva az ujjad[] egy kis blokk vagy egység felemeléséhez. hint.payloadDrop = Nyomd le a [accent]][] gombot a rakomány lerakásához. hint.payloadDrop.mobile = [accent]Koppints és tartsd lenyomva az ujjad[] egy üres területen a rakomány lerakásához. hint.waveFire = A vizet lőszerként használó [accent]Wave[] lövegtornyok automatikusan eloltják a közeli tüzeket. -hint.generator = Az  [accent]égetőerőmű[] szenet éget, és áramot ad át a vele érintkező épületeknek.\n\nAz áramszállítás távolsága [accent]villanyoszlopokkal[] növelhető. -hint.guardian = Az [accent]Őrzők[] páncélozottak. A gyenge lövedékek, mint a [accent]réz[] vagy az [accent]ólom[] [scarlet]nem hatásosak[] az Őrző páncéljával szemben.\n\nHasználj magasabb szintű lövegtornyokat, vagy  [accent]grafitot[] a Duo/Salvo lövegtornyokban, hogy leszedd az Őrzőket. -hint.coreUpgrade = A támaszpont úgy fejleszthető, hogy [accent]magasabb szintű támaszpontot teszel rá[].\n\nHelyezz egy  [accent]Alapítvány[] támaszpontot a  [accent]Szilánk[] támaszpontra. Figyelj rá, hogy ne legyenek az új támaszpont területén épületek. +hint.generator = Az \uf879 [accent]égetőerőmű[] szenet éget, és áramot ad át a vele érintkező épületeknek.\n\nAz áramszállítás távolsága [accent]villanyoszlopokkal[] növelhető. +hint.guardian = Az [accent]Őrzők[] páncélozottak. A gyenge lövedékek, mint a [accent]réz[] vagy az [accent]ólom[] [scarlet]nem hatásosak[] az Őrző páncéljával szemben.\n\nHasználj magasabb szintű lövegtornyokat, vagy \uf835 [accent]grafitot[] a Duo/\uf859Salvo lövegtornyokban, hogy leszedd az Őrzőket. +hint.coreUpgrade = A támaszpont úgy fejleszthető, hogy [accent]magasabb szintű támaszpontot teszel rá[].\n\nHelyezz egy \uf868 [accent]Alapítvány[] támaszpontot a \uf869 [accent]Szilánk[] támaszpontra. Figyelj rá, hogy ne legyenek az új támaszpont területén épületek. hint.presetLaunch = A szürke [accent]landolási zónát tartalmazó szektorokba[], amilyen például a [accent]Fagyott erdő[], bárhonnan kilőhetsz. Nem szükséges hozzá szomszédos területet elfoglalnod.\n\nA [accent]számozott szektorok[], mint ez is, [accent]nem kötelezők[]. hint.presetDifficulty = Ebben a szektorban [scarlet]magas az ellenséges fenyegetettségi szint[].\nAz ilyen szektorokba való indulás [accent]nem ajánlott[] megfelelő technológia és felkészülés nélkül. hint.coreIncinerate = Ha a támaszpont egy nyersanyagból elérte a maximumot, a beérkező további nyersanyagok azonnal [accent]megsemmisítésre kerülnek[]. hint.factoryControl = Egy egységgyár [accent]kimeneti célpontjának[] beállításához kattints parancs módban egy gyárépületre, majd kattints jobb egérgombbal egy helyre.\nAz előállított egységek automatikusan odamennek. hint.factoryControl.mobile = Egy egységgyár [accent]kimeneti célpontjának[] beállításához koppints parancs módban egy gyárépületre, majd koppints egy helyre.\nAz előállított egységek automatikusan odamennek. -gz.mine = Menj a földön lévő  [accent]rézérc[] közelébe, és kattints a bányászat megkezdéséhez. -gz.mine.mobile = Menj a földön lévő  [accent]rézérc[] közelébe, és koppints a bányászat megkezdéséhez. -gz.research = Nyisd meg a  Fejlesztési fát.\nFejleszd ki a  [accent]Mechanikus fúrót[], majd válaszd ki a jobb alsó sarokban lévő menüből.\nKattints egy rézfoltra az elhelyezéséhez. -gz.research.mobile = Nyisd meg a  Fejlesztési fát.\nFejleszd ki a  [accent]Mechanikus fúrót[], majd válaszd ki a jobb alsó sarokban lévő menüből.\nKattints egy rézfoltra az elhelyezéséhez.\n\nA megerősítéshez nyomd meg a jobb alsó sarokban lévő  [accent]pipát[]. -gz.conveyors = Fejleszd ki, és építs  [accent]szállítószalagokat[], hogy a kitermelt\nnyersanyagokat eljuttasd a fúróktól a támaszpontba.\n\nKattints és húzd az egeret, hogy több szállítószalagot helyezz el.\nHasználd a [accent]görgőt[] a forgatáshoz. -gz.conveyors.mobile = Fejleszd ki, és építs  [accent]szállítószalagokat[], hogy a kitermelt\nnyersanyagokat eljuttasd a fúróktól a támaszpontba.\n\nTartsd lenyomva az ujjad és húzd el, hogy több szállítószalagot helyezz el. +gz.mine = Menj a földön lévő \uf8c4 [accent]rézérc[] közelébe, és kattints a bányászat megkezdéséhez. +gz.mine.mobile = Menj a földön lévő \uf8c4 [accent]rézérc[] közelébe, és koppints a bányászat megkezdéséhez. +gz.research = Nyisd meg a \ue875 Fejlesztési fát.\nFejleszd ki a \uf870 [accent]Mechanikus fúrót[], majd válaszd ki a jobb alsó sarokban lévő menüből.\nKattints egy rézfoltra az elhelyezéséhez. +gz.research.mobile = Nyisd meg a \ue875 Fejlesztési fát.\nFejleszd ki a \uf870 [accent]Mechanikus fúrót[], majd válaszd ki a jobb alsó sarokban lévő menüből.\nKattints egy rézfoltra az elhelyezéséhez.\n\nA megerősítéshez nyomd meg a jobb alsó sarokban lévő \ue800 [accent]pipát[]. +gz.conveyors = Fejleszd ki, és építs \uf896 [accent]szállítószalagokat[], hogy a kitermelt\nnyersanyagokat eljuttasd a fúróktól a támaszpontba.\n\nKattints és húzd az egeret, hogy több szállítószalagot helyezz el.\nHasználd a [accent]görgőt[] a forgatáshoz. +gz.conveyors.mobile = Fejleszd ki, és építs \uf896 [accent]szállítószalagokat[], hogy a kitermelt\nnyersanyagokat eljuttasd a fúróktól a támaszpontba.\n\nTartsd lenyomva az ujjad és húzd el, hogy több szállítószalagot helyezz el. gz.drills = Bővítsd a bányászati kapacitást.\nÉpíts több mechanikus fúrót.\nBányássz 100 rezet. -gz.lead = Az  [accent]ólom[] egy másik gyakran használt nyersanyag.\nÉpíts fúrókat az ólom kitermelésére. -gz.moveup =  Menj tovább a további utasításokért. -gz.turrets = Fejleszd ki, és építs 2  [accent]Duo[] lövegtornyot, hogy megvédd a támaszpontot.\nA Duo lövegtornyoknak  [accent]lőszerre[] van szükségük, mely szállítószalaggal juttatható el hozzájuk. +gz.lead = Az \uf837 [accent]ólom[] egy másik gyakran használt nyersanyag.\nÉpíts fúrókat az ólom kitermelésére. +gz.moveup = \ue804 Menj tovább a további utasításokért. +gz.turrets = Fejleszd ki, és építs 2 \uf861 [accent]Duo[] lövegtornyot, hogy megvédd a támaszpontot.\nA Duo lövegtornyoknak \uf838 [accent]lőszerre[] van szükségük, mely szállítószalaggal juttatható el hozzájuk. gz.duoammo = Szállítószalagok segítségével lásd el [accent]rézzel[] a Duo lövegtornyokat. -gz.walls = A [accent]falak[] megakadályozhatják, hogy az épületekben károk keletkezzenek.\nÉpíts  [accent]rézfalakat[] a lövegtornyok köré. +gz.walls = A [accent]falak[] megakadályozhatják, hogy az épületekben károk keletkezzenek.\nÉpíts \uf8ae [accent]rézfalakat[] a lövegtornyok köré. gz.defend = Az ellenség közeledik, készülj fel a védekezésre. -gz.aa = A repülő egységeket nem lehet könnyen elintézni a hagyományos lövegtornyokkal.\nA  [accent]Scatter[] lövegtornyok kiváló légelhárítást biztosítanak, de lőszerként  [accent]ólomra[] van szükségük. -gz.scatterammo = Szállítószalagok segítségével lásd el  [accent]ólommal[] a Scatter lövegtornyokat. +gz.aa = A repülő egységeket nem lehet könnyen elintézni a hagyományos lövegtornyokkal.\nA \uf860 [accent]Scatter[] lövegtornyok kiváló légelhárítást biztosítanak, de lőszerként \uf837 [accent]ólomra[] van szükségük. +gz.scatterammo = Szállítószalagok segítségével lásd el \uf837 [accent]ólommal[] a Scatter lövegtornyokat. gz.supplyturret = [accent]Lövegtorony ellátása gz.zone1 = Ez az ellenség leszállóhelye. gz.zone2 = Bármi, ami a hatósugarában épült, elpusztul, amikor egy hullám elindul. gz.zone3 = Egy hullám most kezdődik.\nKészülj fel. gz.finish = Építs több lövegtornyot, bányássz több nyersanyagot,\nés védekezz az ellenséges hullámok ellen, hogy [accent]elfoglald a szektort[]. -onset.mine = Kattints bal egérgombbal a  [accent]berillium[] kibányászáshoz a falakból.\n\nA mozgáshoz használd a [accent][[WASD] gombokat. -onset.mine.mobile = Koppints a  [accent]berillium[] kibányászáshoz a falakból. -onset.research = Nyisd meg a  fejlesztési fát.\nFejleszd ki, és építs egy  [accent]kondenzációs turbinát[] a kürtőn.\nEz [accent]áramot[] fog termelni. -onset.bore = Fejleszd ki, és építs egy  [accent]plazmafúrót[].\nEz automatikusan bányássza ki a nyersanyagokat a falakból. -onset.power = Ahhoz, hogy [accent]árammal[] lásd el a plazmafúrót, fejleszd ki, és helyezz el egy  [accent]sugárcsomópontot[].\nSegítségükkel összekötheted a kondenzációs turbinát a plazmafúróval. -onset.ducts = Fejleszd ki, és építs  [accent]szállítószalagot[], hogy a kitermelt nyersanyagokat eljuttasd a plazmafúrótól a támaszpontba.\nKattints, és húzd az egeret több szállítószalag elhelyezéséhez.\nHasználd a [accent]görgőt[] a forgatáshoz. -onset.ducts.mobile = Fejleszd ki, és építs  [accent]szállítószalagot[], hogy a kitermelt nyersanyagokat eljuttasd a plazmafúrótól a támaszpontba.\n\nTartsd lenyomva az ujjad és húzd el, hogy több szállítószalagot helyezz el. +onset.mine = Kattints bal egérgombbal a \uf748 [accent]berillium[] kibányászáshoz a falakból.\n\nA mozgáshoz használd a [accent][[WASD] gombokat. +onset.mine.mobile = Koppints a \uf748 [accent]berillium[] kibányászáshoz a falakból. +onset.research = Nyisd meg a \ue875 fejlesztési fát.\nFejleszd ki, és építs egy \uf73e [accent]kondenzációs turbinát[] a kürtőn.\nEz [accent]áramot[] fog termelni. +onset.bore = Fejleszd ki, és építs egy \uf741 [accent]plazmafúrót[].\nEz automatikusan bányássza ki a nyersanyagokat a falakból. +onset.power = Ahhoz, hogy [accent]árammal[] lásd el a plazmafúrót, fejleszd ki, és helyezz el egy \uf73d [accent]sugárcsomópontot[].\nSegítségükkel összekötheted a kondenzációs turbinát a plazmafúróval. +onset.ducts = Fejleszd ki, és építs \uf799 [accent]szállítószalagot[], hogy a kitermelt nyersanyagokat eljuttasd a plazmafúrótól a támaszpontba.\nKattints, és húzd az egeret több szállítószalag elhelyezéséhez.\nHasználd a [accent]görgőt[] a forgatáshoz. +onset.ducts.mobile = Fejleszd ki, és építs \uf799 [accent]szállítószalagot[], hogy a kitermelt nyersanyagokat eljuttasd a plazmafúrótól a támaszpontba.\n\nTartsd lenyomva az ujjad és húzd el, hogy több szállítószalagot helyezz el. onset.moremine = Bővítsd a bányászati kapacitást.\nHelyezz el több plazmavágót, és a támogatásukhoz használj sugárcsomópontokat és szállítószalagokat.\nBányássz 200 berilliumot. -onset.graphite = Az összetettebb épületekhez  [accent]grafit[] szükséges.\nÉpíts plazmavágókat a grafit kibányászásához. -onset.research2 = Kezdd el a [accent]gyárak[] fejlesztését.\nFejleszd ki a  [accent]sziklazúzót[] és a  [accent]szilícium ívkemencét[]. -onset.arcfurnace = A Szilícium ívkemencének  [accent]homokra[] és  [accent]grafitra[] van szüksége, hogy  [accent]szilíciumot[] gyártson.\nTovábbá [accent]áram[] is szükséges a működéséhez. -onset.crusher = Használj  [accent]sziklazúzókat[], hogy homokot bányász. -onset.fabricator = Használd az [accent]egységeket[], hogy felfedezd a pályát, megvédd az épületeket, és megtámadhasd velük az ellenséget. Fejleszd ki, és helyezz el egy  [accent]tankgyárat[]. +onset.graphite = Az összetettebb épületekhez \uf835 [accent]grafit[] szükséges.\nÉpíts plazmavágókat a grafit kibányászásához. +onset.research2 = Kezdd el a [accent]gyárak[] fejlesztését.\nFejleszd ki a \uf74d [accent]sziklazúzót[] és a \uf779 [accent]szilícium ívkemencét[]. +onset.arcfurnace = A Szilícium ívkemencének \uf834 [accent]homokra[] és \uf835 [accent]grafitra[] van szüksége, hogy \uf82f [accent]szilíciumot[] gyártson.\nTovábbá [accent]áram[] is szükséges a működéséhez. +onset.crusher = Használj \uf74d [accent]sziklazúzókat[], hogy homokot bányász. +onset.fabricator = Használd az [accent]egységeket[], hogy felfedezd a pályát, megvédd az épületeket, és megtámadhasd velük az ellenséget. Fejleszd ki, és helyezz el egy \uf6a2 [accent]tankgyárat[]. onset.makeunit = Állíts elő egy egységet.\nHasználd a „?” gombot, hogy megnézd a kiválasztott gyár követelményeit. -onset.turrets = Az egységek hatékonyak, de hatásosan alkalmazva a [accent]lövegtornyok[] jobb védelmi képességeket biztosítanak.\nHelyezz el egy  [accent]Breach[] lövegtornyot.\nA lövegtornyoknak  [accent]lőszerre[] van szüksége. +onset.turrets = Az egységek hatékonyak, de hatásosan alkalmazva a [accent]lövegtornyok[] jobb védelmi képességeket biztosítanak.\nHelyezz el egy \uf6eb [accent]Breach[] lövegtornyot.\nA lövegtornyoknak \uf748 [accent]lőszerre[] van szüksége. onset.turretammo = Szállítótalagok használatával lásd el a lövegtornyokat [accent]berillium[] lőszerrel. -onset.walls = A [accent]falak[] megakadályozhatják, hogy az épületekben károk keletkezzenek.\nÉpíts  [accent]Berillium falakat[] a lövegtornyok körül. +onset.walls = A [accent]falak[] megakadályozhatják, hogy az épületekben károk keletkezzenek.\nÉpíts \uf6ee [accent]Berillium falakat[] a lövegtornyok körül. onset.enemies = Az ellenség közeledik, készülj fel a védekezésre. onset.defenses = [accent]Állíts fel védelmet:[lightgray] {0} onset.attack = Az ellenség most sebezhető. Indítsd ellentámadást. -onset.cores = Új támaszpont csak a [accent]támaszpontcsempére[] helyezhető.\nAz új támaszpontok előretolt bázisként működnek, és megosztják a nyersanyagkészletüket más támaszpontokkal.\nHelyezz el egy  támaszpontot. +onset.cores = Új támaszpont csak a [accent]támaszpontcsempére[] helyezhető.\nAz új támaszpontok előretolt bázisként működnek, és megosztják a nyersanyagkészletüket más támaszpontokkal.\nHelyezz el egy \uf725 támaszpontot. onset.detect = Az ellenség 2 percen belül észrevesz téged.\nÁllíts fel védelmet, bányászatot és termelést. onset.commandmode = Tartsd nyomva a [accent]Shift[] gombot, hogy [accent]parancs módba[] lépj.\n[accent]Bal egérgombbal és húzással[] lehet egységeket kijelölni.\n[accent]Jobb egérgombbal[] utasíthatók az egységek mozgásra vagy támadásra. onset.commandmode.mobile = Nyomd meg a [accent]parancs gombot[], hogy [accent]parancs módba[] lépj.\nTartsd nyomva az ujjad, majd [accent]húzd[] az egységek kiválasztásához.\n[accent]Koppintással[] utasíthatók az egységek mozgásra vagy támadásra. @@ -1985,7 +1985,7 @@ liquid.nitrogen.description = A nyersanyagok kitermelésénél, gáztermelésné liquid.neoplasm.description = A neoplázia reaktor veszélyes biológiai mellékterméke. Gyorsan átterjed minden szomszédos víztartalmú blokkra, amelyhez hozzáér, és közben károsítja azokat. Sűrű folyadék. liquid.neoplasm.details = Neoplazma. Egy kontrollálhatatlan, gyorsan osztódó, iszap állagú, szintetikus sejtmassza. Hőálló. Rendkívül veszélyes minden vízzel kapcsolatos szerkezetre.\n\nTúl összetett és instabil a szabványos elemzésekhez. Potenciális alkalmazási területe ismeretlen. A salakmedencékben való elégetés ajánlott. -block.derelict =  [lightgray]Elhagyatott +block.derelict = \uf77e [lightgray]Elhagyatott block.armored-conveyor.description = Nyersanyagokat szállít. Nem fogad el oldalról nem szállítószalagról érkező nyersanyagot. block.illuminator.description = Világít. block.message.description = Üzenetet tárol a szövetségesek kommunikációjához. @@ -2036,7 +2036,7 @@ block.force-projector.description = Hatszögletű erőteret hoz létre maga kör block.shock-mine.description = Elektromos kisülést hoz létre, ha ellenséggel érintkezik. block.conveyor.description = Nyersanyagokat szállít. block.titanium-conveyor.description = Nyersanyagokat szállít. Gyorsabb a sima szállítószalagnál. -block.plastanium-conveyor.description = Nyersanyagokat szállít tömbösítve. Hátulról fogadja a nyersanyagokat, elöl három irányba osztja szét őket. Több kezdő- és végponttal növelhető az áteresztőképessége. +block.plastanium-conveyor.description = Nyersanyagokat szállít tömbösítve. Hátulról fogadja a nyersanyagokat, elöl három irányba osztja szét őket. Több kezdő- és végponttal növelhető az áteresztőképessége. block.junction.description = Hídként működik két egymást keresztező szállítószalag között. block.bridge-conveyor.description = Nyersanyagokat szállít épületek és terepakadályok fölött. block.phase-conveyor.description = Nyersanyagokat szállít épületek és terepakadályok fölött. Nagyobb távolságra ér, mint a sima szállítószalaghíd, de áramot igényel. @@ -2065,7 +2065,7 @@ block.power-node-large.description = Nagyobb villanyoszlop, nagyobb hatótávols block.surge-tower.description = Hosszútávú villanyoszlop, kevesebb elérhető kapcsolattal. block.diode.description = Az eltárolt áramot irányítja egy irányba továbbítja, de csak akkor, ha a fogadó oldalon kevesebb van tárolva. block.battery.description = Áramot tárol el, ha túltermelés van. Leadja az áramot, ha hiány van. -block.battery-large.description = Áramot tárol el, ha túltermelés van. Leadja az áramot, ha hiány van. Nagyobb kapacitású a szokásos akkumulátornál. +block.battery-large.description = Áramot tárol el, ha túltermelés van. Leadja az áramot, ha hiány van. Nagyobb kapacitású a szokásos akkumulátornál. block.combustion-generator.description = Áramot termel éghető anyagok, például szén, elégetésével. block.thermal-generator.description = Forró környezetben áramot termel. block.steam-generator.description = Éghető anyagok elégetésével és víz gőzzé alakításával áramot termel. @@ -2115,7 +2115,7 @@ block.parallax.description = Vonónyalábot bocsát ki, amivel magához vonzza, block.tsunami.description = Erős folyadékhullámot lő az ellenségre. Eloltja a tüzeket, ha vízzel van ellátva. block.silicon-crucible.description = Szilíciumot finomít homokból és szénből, piratitot használ kiegészítő hőforrásként. Forró környezetben még hatékonyabb. block.disassembler.description = Ritka ásványi összetevőket válogat ki a salakból, alacsony hatékonysággal. Képes tóriumot kiválogatni. -block.overdrive-dome.description = Megnöveli a környező épületek termelési sebességét. A működtetése tóritkvarcot és szilíciumot igényel. +block.overdrive-dome.description = Megnöveli a környező épületek termelési sebességét. A működtetése tóritkvarcot és szilíciumot igényel. block.payload-conveyor.description = Nagy mennyiségű terhet mozgatni, például gyárakból érkező nyersanyagokat. Mágneses. Használható súlytalanságban. block.payload-router.description = Háromfelé osztja szét a beérkező terhet. Rendezőként is szolgál, ha van megadva szűrő. Mágneses. Használható súlytalanságban. block.ground-factory.description = Földi egységeket gyárt. A kész egységek azonnal hadra foghatók, vagy újratervezőkben továbbfejleszthetők. @@ -2292,7 +2292,7 @@ unit.emanate.description = Az Akropolisz védelmére szolgáló építményeket lst.read = Szám kiolvasása egy összekapcsolt memóriacellából. lst.write = Szám beírása egy összekapcsolt memóriacellába. lst.print = Szöveg hozzáadása a nyomtatási pufferhez.\nA [accent]Print Flush[] használatáig nem jelenít meg semmit. -lst.format = A szövegpufferben lévő következő helyőrző cseréje egy értékre.\nNem csinál semmit, ha a helyőrzőminta érvénytelen.\nHelyőrzőminta: "{[accent]number 0-9[]}"\nPélda:\n[accent]print "test {0}"\nformat "example" +lst.format = A szövegpufferben lévő következő helyőrző cseréje egy értékre.\nNem csinál semmit, ha a helyőrzőminta érvénytelen.\nHelyőrzőminta: "{[accent]number 0-9[]}"\nPélda:\n[accent]print "test {0}"\nformat "example" lst.draw = Művelet hozzáadása a rajzpufferhez.\nA [accent]Draw Flush[] használatáig nem jelenít meg semmit. lst.drawflush = Sorba állított [accent]Draw[] műveletek megjelenítése a kijelzőn. lst.printflush = Sorba állított [accent]Print[] műveletek kiírása egy üzenetblokkba. diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index 5d055120e0..8c757e81b6 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -1857,6 +1857,7 @@ hint.launch = Quando recursos suficientes forem coletados, você pode [accent]La hint.launch.mobile = Quando recursos suficientes forem coletados, você pode [accent]Lançar[] selecionando setores próximos a partir do \ue827 [accent]Mapa[] no \ue88c [accent]Menu[]. hint.schematicSelect = Segure [accent][[F][] e arraste para selecionar blocos para copiar e colar.\n\n[accent][[Middle Click][] para copiar um bloco só. hint.rebuildSelect = Segure [accent][[B][] e arraste para selecionar blocos destruídos.\nIsso irá reconstruí-los automaticamente. +hint.rebuildSelect.mobile = Select the \ue874 copy button, then tap the \ue80f rebuild button and drag to select destroyed block plans.\nThis will rebuild them automatically. hint.conveyorPathfind = Segure [accent][[L-Ctrl][] enquanto arrasta as esteiras para gerar automaticamente um caminho. hint.conveyorPathfind.mobile = Ative o \ue844 [accent]modo diagonal[] e arraste as esteiras para gerar automaticamente um caminho. hint.boost = Segure [accent][[L-Shift][] para voar sobre obstáculos com a sua unidade.\n\nApenas algumas unidades terrestres tem propulsores. @@ -1912,9 +1913,13 @@ onset.turretammo = Abasteça a torreta com [accent]munição de berílio.[] onset.walls = [accent]Muros[] podem previnir danos recebidos de atingir as construções.\nColoque \uf6ee [accent]muros de berílio[] em volta das torretas. onset.enemies = Inimigo vindo, se prepare. +onset.defenses = [accent]Set up defenses:[lightgray] {0} onset.attack = O inimigo está vulnerável. Contra ataque. onset.cores = Novos núcleos podem ser colocados em [accent]ladrilhos de núcleo[].\nNovos núcleos funcionam como bases avançadas e compartilham seus recursos com outros núcleos.\nColoque um \uf725 núcleo. onset.detect = O inimigo poderá te detectar em 2 minutos.\nConstrua defesas, mineração e produção. +onset.commandmode = Hold [accent]shift[] to enter [accent]command mode[].\n[accent]Left-click and drag[] to select units.\n[accent]Right-click[] to order selected units to move or attack. +onset.commandmode.mobile = Press the [accent]command button[] to enter [accent]command mode[].\nHold down a finger, then [accent]drag[] to select units.\n[accent]Tap[] to order selected units to move or attack. +aegis.tungsten = Tungsten can be mined using an [accent]impact drill[].\nThis structure requires [accent]water[] and [accent]power[]. split.pickup = Some blocks can be picked up by the core unit.\nPick up this [accent]container[] and place it onto the [accent]payload loader[].\n(Default keys are [ and ] to pick up and drop) split.pickup.mobile = Some blocks can be picked up by the core unit.\nPick up this [accent]container[] and place it onto the [accent]payload loader[].\n(To pick up or drop something, long-press it.) From d8c1ea17e13249ab20bee88fcc311e9e191806bd Mon Sep 17 00:00:00 2001 From: MEEPofFaith <54301439+MEEPofFaith@users.noreply.github.com> Date: Fri, 29 Mar 2024 12:17:19 -0700 Subject: [PATCH 048/348] Make payload blocks run `onDestroyed()` of the block payload it carries when destroyed. (#8253) * Payloads go boom. * When a payload unit dies too * Keep the functionality, but limit it to a rule --- core/assets/bundles/bundle.properties | 1 + core/src/mindustry/entities/comp/PayloadComp.java | 5 +++++ core/src/mindustry/game/Rules.java | 2 ++ core/src/mindustry/ui/dialogs/CustomRulesDialog.java | 1 + core/src/mindustry/world/blocks/payloads/BuildPayload.java | 5 +++++ core/src/mindustry/world/blocks/payloads/Payload.java | 2 ++ core/src/mindustry/world/blocks/payloads/PayloadBlock.java | 6 ++++++ .../mindustry/world/blocks/payloads/PayloadConveyor.java | 6 ++++++ 8 files changed, 28 insertions(+) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 260c5af651..af18ff5f20 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -1313,6 +1313,7 @@ rules.unitdamagemultiplier = Unit Damage Multiplier rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Solar Power Multiplier rules.unitcapvariable = Cores Contribute To Unit Cap +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Base Unit Cap rules.limitarea = Limit Map Area rules.enemycorebuildradius = Enemy Core No-Build Radius:[lightgray] (tiles) diff --git a/core/src/mindustry/entities/comp/PayloadComp.java b/core/src/mindustry/entities/comp/PayloadComp.java index d1f7518e65..9b9d6e6405 100644 --- a/core/src/mindustry/entities/comp/PayloadComp.java +++ b/core/src/mindustry/entities/comp/PayloadComp.java @@ -60,6 +60,11 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{ } } + @Override + public void destroy(){ + if(Vars.state.rules.unitPayloadsExplode) payloads.each(Payload::destroyed); + } + float payloadUsed(){ return payloads.sumf(p -> p.size() * p.size()); } diff --git a/core/src/mindustry/game/Rules.java b/core/src/mindustry/game/Rules.java index 951426907d..9da173c2df 100644 --- a/core/src/mindustry/game/Rules.java +++ b/core/src/mindustry/game/Rules.java @@ -59,6 +59,8 @@ public class Rules{ public boolean unitAmmo = false; /** EXPERIMENTAL! If true, blocks will update in units and share power. */ public boolean unitPayloadUpdate = false; + /** If true, units' payloads are destroy()ed when the unit is destroyed. */ + public boolean unitPayloadsExplode = false; /** Whether cores add to unit limit */ public boolean unitCapVariable = true; /** If true, unit spawn points are shown. */ diff --git a/core/src/mindustry/ui/dialogs/CustomRulesDialog.java b/core/src/mindustry/ui/dialogs/CustomRulesDialog.java index 4c54569b4d..1091c74c08 100644 --- a/core/src/mindustry/ui/dialogs/CustomRulesDialog.java +++ b/core/src/mindustry/ui/dialogs/CustomRulesDialog.java @@ -233,6 +233,7 @@ public class CustomRulesDialog extends BaseDialog{ title("@rules.title.unit"); check("@rules.unitcapvariable", b -> rules.unitCapVariable = b, () -> rules.unitCapVariable); + check("@rules.unitpayloadsexplode", b -> rules.unitPayloadsExplode = b, () -> rules.unitPayloadsExplode); numberi("@rules.unitcap", f -> rules.unitCap = f, () -> rules.unitCap, -999, 999); number("@rules.unitdamagemultiplier", f -> rules.unitDamageMultiplier = f, () -> rules.unitDamageMultiplier); number("@rules.unitcrashdamagemultiplier", f -> rules.unitCrashDamageMultiplier = f, () -> rules.unitCrashDamageMultiplier); diff --git a/core/src/mindustry/world/blocks/payloads/BuildPayload.java b/core/src/mindustry/world/blocks/payloads/BuildPayload.java index 42f79374ad..90e37e9e16 100644 --- a/core/src/mindustry/world/blocks/payloads/BuildPayload.java +++ b/core/src/mindustry/world/blocks/payloads/BuildPayload.java @@ -51,6 +51,11 @@ public class BuildPayload implements Payload{ build.updatePayload(unitHolder, buildingHolder); } + @Override + public void destroyed(){ + build.onDestroyed(); + } + @Override public ItemStack[] requirements(){ return build.block.requirements; diff --git a/core/src/mindustry/world/blocks/payloads/Payload.java b/core/src/mindustry/world/blocks/payloads/Payload.java index 218fab7192..b1e6f90ff5 100644 --- a/core/src/mindustry/world/blocks/payloads/Payload.java +++ b/core/src/mindustry/world/blocks/payloads/Payload.java @@ -54,6 +54,8 @@ public interface Payload extends Position{ return 0f; } + default void destroyed(){}; + /** writes the payload for saving. */ void write(Writes write); diff --git a/core/src/mindustry/world/blocks/payloads/PayloadBlock.java b/core/src/mindustry/world/blocks/payloads/PayloadBlock.java index 1ecd627dc3..b4b0de0f37 100644 --- a/core/src/mindustry/world/blocks/payloads/PayloadBlock.java +++ b/core/src/mindustry/world/blocks/payloads/PayloadBlock.java @@ -151,6 +151,12 @@ public class PayloadBlock extends Block{ } } + @Override + public void onDestroyed(){ + if(payload != null) payload.destroyed(); + super.onDestroyed(); + } + public boolean blends(int direction){ return PayloadBlock.blends(this, direction); } diff --git a/core/src/mindustry/world/blocks/payloads/PayloadConveyor.java b/core/src/mindustry/world/blocks/payloads/PayloadConveyor.java index 6fedce44a3..e6c368d97e 100644 --- a/core/src/mindustry/world/blocks/payloads/PayloadConveyor.java +++ b/core/src/mindustry/world/blocks/payloads/PayloadConveyor.java @@ -190,6 +190,12 @@ public class PayloadConveyor extends Block{ super.draw(); } + @Override + public void onDestroyed(){ + if(item != null) item.destroyed(); + super.onDestroyed(); + } + @Override public void draw(){ super.draw(); From 2fb5bc56c6c5cd9a65eb5064153a2a3e30cf4173 Mon Sep 17 00:00:00 2001 From: MEEPofFaith <54301439+MEEPofFaith@users.noreply.github.com> Date: Fri, 29 Mar 2024 21:11:39 -0700 Subject: [PATCH 049/348] Better Ability Stats (#9654) * Fix armor plate multiplier + change Math.round to Strings.autoFixed * Missing ability name bundles * Center ability name * SuppressionFieldAbility stats * Is two per row is acceptable? I can revert this commit if not. * LiquidExplodeAbility stat display * MoveLightningAbility stat display * Better SpawnDeathAbility display * Fix multiplier coloring inconsistencies Some had [lightgray] before %/x, some had it after * Consistent content name display Match with bullet status effects * Consistent stat formatting Convert from some being "stat: #" and some being "# stat" to all being "# stat" * Re-order stats * Optimize Imports * Add ability descriptions * Apparently I forgot LiquidRegenAbility * Mention healing allies if displayHeal = true --- core/assets/bundles/bundle.properties | 46 +++++++++++++++---- core/src/mindustry/content/UnitTypes.java | 2 +- .../mindustry/entities/abilities/Ability.java | 18 +++++++- .../entities/abilities/ArmorPlateAbility.java | 6 +-- .../abilities/EnergyFieldAbility.java | 29 +++++++----- .../entities/abilities/ForceFieldAbility.java | 12 ++--- .../abilities/LiquidExplodeAbility.java | 7 +++ .../abilities/LiquidRegenAbility.java | 9 ++++ .../abilities/MoveLightningAbility.java | 13 +++++- .../entities/abilities/RegenAbility.java | 17 +++---- .../abilities/RepairFieldAbility.java | 9 ++-- .../entities/abilities/ShieldArcAbility.java | 9 ++-- .../abilities/ShieldRegenFieldAbility.java | 13 +++--- .../entities/abilities/SpawnDeathAbility.java | 3 +- .../abilities/StatusFieldAbility.java | 13 +++--- .../abilities/SuppressionFieldAbility.java | 18 ++++++++ .../entities/abilities/UnitSpawnAbility.java | 6 +-- core/src/mindustry/world/meta/StatValues.java | 32 +++++++------ 18 files changed, 182 insertions(+), 80 deletions(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index af18ff5f20..94d1e5d086 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -998,17 +998,47 @@ stat.immunities = Immunities stat.healing = Healing ability.forcefield = Force Field +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Repair Field +ability.repairfield.description = Repairs nearby units ability.statusfield = Status Field +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Factory +ability.unitspawn.description = Constructs units ability.shieldregenfield = Shield Regen Field +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Movement Lightning +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Shield Arc +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Repair Suppression +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energy Field -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} -ability.regen = Regeneration +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies +ability.regen = Self Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death + +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Only Core Depositing Allowed bar.drilltierreq = Better Drill Required @@ -1051,15 +1081,15 @@ bullet.armorpierce = [stat]armor piercing bullet.maxdamagefraction = [stat]{0}%[lightgray] damage limit bullet.suppression = [stat]{0}[lightgray] seconds of repair suppression ~ [stat]{1}[lightgray] tiles bullet.interval = [stat]{0}/sec[lightgray] interval bullets: -bullet.frags = [stat]{0}[lightgray]x frag bullets: -bullet.lightning = [stat]{0}[lightgray]x lightning ~ [stat]{1}[lightgray] damage +bullet.frags = [stat]{0}x[lightgray] frag bullets: +bullet.lightning = [stat]{0}x[lightgray] lightning ~ [stat]{1}[lightgray] damage bullet.buildingdamage = [stat]{0}%[lightgray] building damage bullet.knockback = [stat]{0}[lightgray] knockback -bullet.pierce = [stat]{0}[lightgray]x pierce +bullet.pierce = [stat]{0}x[lightgray] pierce bullet.infinitepierce = [stat]pierce -bullet.healpercent = [stat]{0}[lightgray]% repair +bullet.healpercent = [stat]{0}%[lightgray] repair bullet.healamount = [stat]{0}[lightgray] direct repair -bullet.multiplier = [stat]{0}[lightgray]x ammo multiplier +bullet.multiplier = [stat]{0}x[lightgray] ammo multiplier bullet.reload = [stat]{0}%[lightgray] fire rate bullet.range = [stat]{0}[lightgray] tiles range diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index c9200be5e1..015b069c9f 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -3895,7 +3895,7 @@ public class UnitTypes{ x = 43f * i / 4f; particles = parts; //visual only, the middle one does the actual suppressing - display = active = false; + active = false; }}); } diff --git a/core/src/mindustry/entities/abilities/Ability.java b/core/src/mindustry/entities/abilities/Ability.java index 5a6b025417..42a31d6312 100644 --- a/core/src/mindustry/entities/abilities/Ability.java +++ b/core/src/mindustry/entities/abilities/Ability.java @@ -6,6 +6,7 @@ import mindustry.gen.*; import mindustry.type.*; public abstract class Ability implements Cloneable{ + protected static final float descriptionWidth = 350f; /** If false, this ability does not show in unit stats. */ public boolean display = true; //the one and only data variable that is synced. @@ -16,7 +17,16 @@ public abstract class Ability implements Cloneable{ public void death(Unit unit){} public void init(UnitType type){} public void displayBars(Unit unit, Table bars){} - public void addStats(Table t){} + public void addStats(Table t){ + if(Core.bundle.has(getBundle() + ".description")){ + t.add(Core.bundle.get(getBundle() + ".description")).wrap().width(descriptionWidth); + t.row(); + } + } + + public String abilityStat(String stat, Object... values){ + return Core.bundle.format("ability.stat." + stat, values); + } public Ability copy(){ try{ @@ -29,7 +39,11 @@ public abstract class Ability implements Cloneable{ /** @return localized ability name; mods should override this. */ public String localized(){ + return Core.bundle.get(getBundle()); + } + + public String getBundle(){ var type = getClass(); - return Core.bundle.get("ability." + (type.isAnonymousClass() ? type.getSuperclass() : type).getSimpleName().replace("Ability", "").toLowerCase()); + return "ability." + (type.isAnonymousClass() ? type.getSuperclass() : type).getSimpleName().replace("Ability", "").toLowerCase(); } } diff --git a/core/src/mindustry/entities/abilities/ArmorPlateAbility.java b/core/src/mindustry/entities/abilities/ArmorPlateAbility.java index 4c02028492..d417e01735 100644 --- a/core/src/mindustry/entities/abilities/ArmorPlateAbility.java +++ b/core/src/mindustry/entities/abilities/ArmorPlateAbility.java @@ -4,11 +4,10 @@ import arc.*; import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; -import arc.scene.ui.layout.Table; +import arc.scene.ui.layout.*; import arc.util.*; import mindustry.gen.*; import mindustry.graphics.*; -import mindustry.world.meta.*; public class ArmorPlateAbility extends Ability{ public TextureRegion plateRegion; @@ -39,7 +38,8 @@ public class ArmorPlateAbility extends Ability{ @Override public void addStats(Table t){ - t.add("[lightgray]" + Stat.healthMultiplier.localized() + ": [white]" + Math.round(healthMultiplier * 100f) + 100 + "%"); + super.addStats(t); + t.add(abilityStat("damagereduction", Strings.autoFixed(-healthMultiplier * 100f, 1))); } @Override diff --git a/core/src/mindustry/entities/abilities/EnergyFieldAbility.java b/core/src/mindustry/entities/abilities/EnergyFieldAbility.java index acd47475bc..8c041087c0 100644 --- a/core/src/mindustry/entities/abilities/EnergyFieldAbility.java +++ b/core/src/mindustry/entities/abilities/EnergyFieldAbility.java @@ -14,7 +14,6 @@ import mindustry.game.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.type.*; -import mindustry.world.meta.*; import static mindustry.Vars.*; @@ -53,23 +52,29 @@ public class EnergyFieldAbility extends Ability{ @Override public void addStats(Table t){ - t.add(Core.bundle.format("bullet.damage", damage)); + if(displayHeal){ + t.add(Core.bundle.get(getBundle() + ".healdescription")).wrap().width(descriptionWidth); + }else{ + t.add(Core.bundle.get(getBundle() + ".description")).wrap().width(descriptionWidth); + } t.row(); - t.add("[lightgray]" + Stat.reload.localized() + ": [white]" + Strings.autoFixed(60f / reload, 2) + " " + StatUnit.perSecond.localized()); - t.row(); - t.add("[lightgray]" + Stat.shootRange.localized() + ": [white]" + Strings.autoFixed(range / tilesize, 2) + " " + StatUnit.blocks.localized()); - t.row(); - t.add(Core.bundle.format("ability.energyfield.maxtargets", maxTargets)); + t.add(Core.bundle.format("bullet.range", Strings.autoFixed(range / tilesize, 2))); + t.row(); + t.add(abilityStat("firingrate", Strings.autoFixed(60f / reload, 2))); + t.row(); + t.add(abilityStat("maxtargets", maxTargets)); + t.row(); + t.add(Core.bundle.format("bullet.damage", damage)); + if(status != StatusEffects.none){ + t.row(); + t.add((status.hasEmoji() ? status.emoji() : "") + "[stat]" + status.localizedName); + } if(displayHeal){ t.row(); t.add(Core.bundle.format("bullet.healpercent", Strings.autoFixed(healPercent, 2))); t.row(); - t.add(Core.bundle.format("ability.energyfield.sametypehealmultiplier", Math.round(sameTypeHealMult * 100f))); - } - if(status != StatusEffects.none){ - t.row(); - t.add(status.emoji() + " " + status.localizedName); + t.add(abilityStat("sametypehealmultiplier", (sameTypeHealMult < 1f ? "[negstat]" : "") + Strings.autoFixed(sameTypeHealMult * 100f, 2))); } } diff --git a/core/src/mindustry/entities/abilities/ForceFieldAbility.java b/core/src/mindustry/entities/abilities/ForceFieldAbility.java index 81264ac05c..f391156adb 100644 --- a/core/src/mindustry/entities/abilities/ForceFieldAbility.java +++ b/core/src/mindustry/entities/abilities/ForceFieldAbility.java @@ -1,5 +1,6 @@ package mindustry.entities.abilities; +import arc.*; import arc.func.*; import arc.graphics.*; import arc.graphics.g2d.*; @@ -12,7 +13,6 @@ import mindustry.content.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.ui.*; -import mindustry.world.meta.*; import static mindustry.Vars.*; @@ -73,14 +73,14 @@ public class ForceFieldAbility extends Ability{ @Override public void addStats(Table t){ - t.add("[lightgray]" + Stat.health.localized() + ": [white]" + Math.round(max)); + super.addStats(t); + t.add(Core.bundle.format("bullet.range", Strings.autoFixed(radius / tilesize, 2))); t.row(); - t.add("[lightgray]" + Stat.shootRange.localized() + ": [white]" + Strings.autoFixed(radius / tilesize, 2) + " " + StatUnit.blocks.localized()); + t.add(abilityStat("shield", Strings.autoFixed(max, 2))); t.row(); - t.add("[lightgray]" + Stat.repairSpeed.localized() + ": [white]" + Strings.autoFixed(regen * 60f, 2) + StatUnit.perSecond.localized()); - t.row(); - t.add("[lightgray]" + Stat.cooldownTime.localized() + ": [white]" + Strings.autoFixed(cooldown / 60f, 2) + " " + StatUnit.seconds.localized()); + t.add(abilityStat("repairspeed", Strings.autoFixed(regen * 60f, 2))); t.row(); + t.add(abilityStat("cooldown", Strings.autoFixed(cooldown / 60f, 2))); } @Override diff --git a/core/src/mindustry/entities/abilities/LiquidExplodeAbility.java b/core/src/mindustry/entities/abilities/LiquidExplodeAbility.java index 57b6100f66..cf6b16f6e8 100644 --- a/core/src/mindustry/entities/abilities/LiquidExplodeAbility.java +++ b/core/src/mindustry/entities/abilities/LiquidExplodeAbility.java @@ -1,6 +1,7 @@ package mindustry.entities.abilities; import arc.math.*; +import arc.scene.ui.layout.*; import arc.util.noise.*; import mindustry.content.*; import mindustry.entities.*; @@ -16,6 +17,12 @@ public class LiquidExplodeAbility extends Ability{ public float radAmountScale = 5f, radScale = 1f; public float noiseMag = 6.5f, noiseScl = 5f; + @Override + public void addStats(Table t){ + super.addStats(t); + t.add((liquid.hasEmoji() ? liquid.emoji() : "") + "[stat]" + liquid.localizedName); + } + @Override public void death(Unit unit){ //TODO what if noise is radial, so it looks like a splat? diff --git a/core/src/mindustry/entities/abilities/LiquidRegenAbility.java b/core/src/mindustry/entities/abilities/LiquidRegenAbility.java index 7f7d827f95..6f864035ce 100644 --- a/core/src/mindustry/entities/abilities/LiquidRegenAbility.java +++ b/core/src/mindustry/entities/abilities/LiquidRegenAbility.java @@ -1,6 +1,7 @@ package mindustry.entities.abilities; import arc.math.*; +import arc.scene.ui.layout.*; import arc.util.*; import mindustry.content.*; import mindustry.entities.*; @@ -17,6 +18,14 @@ public class LiquidRegenAbility extends Ability{ public float slurpEffectChance = 0.4f; public Effect slurpEffect = Fx.heal; + @Override + public void addStats(Table t){ + super.addStats(t); + t.add((liquid.hasEmoji() ? liquid.emoji() : "") + "[stat]" + liquid.localizedName); + t.row(); + t.add(abilityStat("slurpheal", Strings.autoFixed(regenPerSlurp, 2))); + } + @Override public void update(Unit unit){ //TODO timer? diff --git a/core/src/mindustry/entities/abilities/MoveLightningAbility.java b/core/src/mindustry/entities/abilities/MoveLightningAbility.java index ab70313df4..c55849690b 100644 --- a/core/src/mindustry/entities/abilities/MoveLightningAbility.java +++ b/core/src/mindustry/entities/abilities/MoveLightningAbility.java @@ -5,12 +5,15 @@ import arc.audio.*; import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; +import arc.scene.ui.layout.*; import arc.util.*; import mindustry.content.*; import mindustry.entities.*; import mindustry.entities.bullet.*; import mindustry.gen.*; +import static mindustry.Vars.*; + public class MoveLightningAbility extends Ability{ /** Lightning damage */ public float damage = 35f; @@ -63,7 +66,15 @@ public class MoveLightningAbility extends Ability{ this.maxSpeed = maxSpeed; this.color = color; } - + + @Override + public void addStats(Table t){ + super.addStats(t); + t.add(abilityStat("minspeed", Strings.autoFixed(minSpeed * 60f / tilesize, 2))); + t.row(); + t.add(Core.bundle.format("bullet.damage", damage)); + } + @Override public void update(Unit unit){ float scl = Mathf.clamp((unit.vel().len() - minSpeed) / (maxSpeed - minSpeed)); diff --git a/core/src/mindustry/entities/abilities/RegenAbility.java b/core/src/mindustry/entities/abilities/RegenAbility.java index 0a7bca80c9..ffcfe50e9d 100644 --- a/core/src/mindustry/entities/abilities/RegenAbility.java +++ b/core/src/mindustry/entities/abilities/RegenAbility.java @@ -1,10 +1,8 @@ package mindustry.entities.abilities; -import arc.Core; import arc.scene.ui.layout.*; import arc.util.*; import mindustry.gen.*; -import mindustry.world.meta.*; public class RegenAbility extends Ability{ /** Amount healed as percent per tick. */ @@ -14,13 +12,16 @@ public class RegenAbility extends Ability{ @Override public void addStats(Table t){ - if(amount > 0.01f){ - t.add("[lightgray]" + Stat.repairSpeed.localized() + ": [white]" + Strings.autoFixed(amount * 60f, 2) + StatUnit.perSecond.localized()); - t.row(); - } + super.addStats(t); - if(percentAmount > 0.01f){ - t.add(Core.bundle.format("bullet.healpercent", Strings.autoFixed(percentAmount * 60f, 2)) + StatUnit.perSecond.localized()); //stupid but works + boolean flat = amount >= 0.001f; + boolean percent = percentAmount >= 0.001f; + + if(flat || percent){ + t.add(abilityStat("regen", + (flat ? Strings.autoFixed(amount * 60f, 2) + (percent ? " [lightgray]+[stat] " : "") : "") + + (percent ? Strings.autoFixed(percentAmount * 60f, 2) + "%" : "") + )); } } diff --git a/core/src/mindustry/entities/abilities/RepairFieldAbility.java b/core/src/mindustry/entities/abilities/RepairFieldAbility.java index fb42cc7018..7c871978cc 100644 --- a/core/src/mindustry/entities/abilities/RepairFieldAbility.java +++ b/core/src/mindustry/entities/abilities/RepairFieldAbility.java @@ -1,13 +1,13 @@ package mindustry.entities.abilities; +import arc.*; import arc.scene.ui.layout.*; import arc.util.*; import mindustry.content.*; import mindustry.entities.*; import mindustry.gen.*; -import mindustry.world.meta.*; -import static mindustry.Vars.tilesize; +import static mindustry.Vars.*; public class RepairFieldAbility extends Ability{ public float amount = 1, reload = 100, range = 60; @@ -28,9 +28,10 @@ public class RepairFieldAbility extends Ability{ @Override public void addStats(Table t){ - t.add("[lightgray]" + Stat.repairSpeed.localized() + ": [white]" + Strings.autoFixed(amount * 60f / reload, 2) + StatUnit.perSecond.localized()); + super.addStats(t); + t.add(Core.bundle.format("bullet.range", Strings.autoFixed(range / tilesize, 2))); t.row(); - t.add("[lightgray]" + Stat.shootRange.localized() + ": [white]" + Strings.autoFixed(range / tilesize, 2) + " " + StatUnit.blocks.localized()); + t.add(abilityStat("repairspeed", Strings.autoFixed(amount * 60f / reload, 2))); } @Override diff --git a/core/src/mindustry/entities/abilities/ShieldArcAbility.java b/core/src/mindustry/entities/abilities/ShieldArcAbility.java index b63a88bf1e..a7d41cdaf7 100644 --- a/core/src/mindustry/entities/abilities/ShieldArcAbility.java +++ b/core/src/mindustry/entities/abilities/ShieldArcAbility.java @@ -13,7 +13,6 @@ import mindustry.gen.*; import mindustry.graphics.*; import mindustry.type.*; import mindustry.ui.*; -import mindustry.world.meta.*; public class ShieldArcAbility extends Ability{ private static Unit paramUnit; @@ -69,12 +68,12 @@ public class ShieldArcAbility extends Ability{ @Override public void addStats(Table t){ - t.add("[lightgray]" + Stat.health.localized() + ": [white]" + Math.round(max)); + super.addStats(t); + t.add(abilityStat("shield", Strings.autoFixed(max, 2))); t.row(); - t.add("[lightgray]" + Stat.repairSpeed.localized() + ": [white]" + Strings.autoFixed(regen * 60f, 2) + StatUnit.perSecond.localized()); - t.row(); - t.add("[lightgray]" + Stat.cooldownTime.localized() + ": [white]" + Strings.autoFixed(cooldown / 60f, 2) + " " + StatUnit.seconds.localized()); + t.add(abilityStat("repairspeed", Strings.autoFixed(regen * 60f, 2))); t.row(); + t.add(abilityStat("cooldown", Strings.autoFixed(cooldown / 60f, 2))); } @Override diff --git a/core/src/mindustry/entities/abilities/ShieldRegenFieldAbility.java b/core/src/mindustry/entities/abilities/ShieldRegenFieldAbility.java index 51d1f79175..c34ce359ce 100644 --- a/core/src/mindustry/entities/abilities/ShieldRegenFieldAbility.java +++ b/core/src/mindustry/entities/abilities/ShieldRegenFieldAbility.java @@ -1,14 +1,13 @@ package mindustry.entities.abilities; -import arc.Core; +import arc.*; import arc.scene.ui.layout.*; import arc.util.*; import mindustry.content.*; import mindustry.entities.*; import mindustry.gen.*; -import mindustry.world.meta.*; -import static mindustry.Vars.tilesize; +import static mindustry.Vars.*; public class ShieldRegenFieldAbility extends Ability{ public float amount = 1, max = 100f, reload = 100, range = 60; @@ -30,12 +29,12 @@ public class ShieldRegenFieldAbility extends Ability{ @Override public void addStats(Table t){ - t.add("[lightgray]" + Core.bundle.get("waves.shields") + ": [white]" + Math.round(max)); //extremely stupid usage + super.addStats(t); + t.add(Core.bundle.format("bullet.range", Strings.autoFixed(range / tilesize, 2))); t.row(); - t.add("[lightgray]" + Stat.shootRange.localized() + ": [white]" + Strings.autoFixed(range / tilesize, 2) + " " + StatUnit.blocks.localized()); - t.row(); - t.add("[lightgray]" + Stat.reload.localized() + ": [white]" + Strings.autoFixed(60f / reload, 2) + " " + StatUnit.perSecond.localized()); + t.add(abilityStat("firingrate", Strings.autoFixed(60f / reload, 2))); t.row(); + t.add(abilityStat("shield", Strings.autoFixed(max, 2))); } @Override diff --git a/core/src/mindustry/entities/abilities/SpawnDeathAbility.java b/core/src/mindustry/entities/abilities/SpawnDeathAbility.java index 8aac58776a..6bc5b0a49d 100644 --- a/core/src/mindustry/entities/abilities/SpawnDeathAbility.java +++ b/core/src/mindustry/entities/abilities/SpawnDeathAbility.java @@ -27,7 +27,8 @@ public class SpawnDeathAbility extends Ability{ @Override public void addStats(Table t){ - t.add((randAmount > 0 ? amount + "-" + (amount + randAmount) : amount) + " " + unit.emoji() + " " + unit.localizedName); + super.addStats(t); + t.add("[stat]" + (randAmount > 0 ? amount + "x-" + (amount + randAmount) : amount) + "x[] " + (unit.hasEmoji() ? unit.emoji() : "") + "[stat]" + unit.localizedName); } @Override diff --git a/core/src/mindustry/entities/abilities/StatusFieldAbility.java b/core/src/mindustry/entities/abilities/StatusFieldAbility.java index 7bc7bcb375..c0ca12d381 100644 --- a/core/src/mindustry/entities/abilities/StatusFieldAbility.java +++ b/core/src/mindustry/entities/abilities/StatusFieldAbility.java @@ -1,15 +1,15 @@ package mindustry.entities.abilities; +import arc.*; import arc.math.*; -import arc.scene.ui.layout.Table; +import arc.scene.ui.layout.*; import arc.util.*; import mindustry.content.*; import mindustry.entities.*; import mindustry.gen.*; import mindustry.type.*; -import mindustry.world.meta.*; -import static mindustry.Vars.tilesize; +import static mindustry.Vars.*; public class StatusFieldAbility extends Ability{ public StatusEffect effect; @@ -33,11 +33,12 @@ public class StatusFieldAbility extends Ability{ @Override public void addStats(Table t){ - t.add("[lightgray]" + Stat.reload.localized() + ": [white]" + Strings.autoFixed(60f / reload, 2) + " " + StatUnit.perSecond.localized()); + super.addStats(t); + t.add(Core.bundle.format("bullet.range", Strings.autoFixed(range / tilesize, 2))); t.row(); - t.add("[lightgray]" + Stat.shootRange.localized() + ": [white]" + Strings.autoFixed(range / tilesize, 2) + " " + StatUnit.blocks.localized()); + t.add(abilityStat("firingrate", Strings.autoFixed(60f / reload, 2))); t.row(); - t.add(effect.emoji() + " " + effect.localizedName); + t.add((effect.hasEmoji() ? effect.emoji() : "") + "[stat]" + effect.localizedName); } @Override diff --git a/core/src/mindustry/entities/abilities/SuppressionFieldAbility.java b/core/src/mindustry/entities/abilities/SuppressionFieldAbility.java index d421ebf848..be69a45797 100644 --- a/core/src/mindustry/entities/abilities/SuppressionFieldAbility.java +++ b/core/src/mindustry/entities/abilities/SuppressionFieldAbility.java @@ -1,12 +1,17 @@ package mindustry.entities.abilities; +import arc.*; import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; +import arc.scene.ui.layout.*; import arc.util.*; import mindustry.entities.*; import mindustry.gen.*; import mindustry.graphics.*; +import mindustry.type.*; + +import static mindustry.Vars.*; public class SuppressionFieldAbility extends Ability{ protected static Rand rand = new Rand(); @@ -33,6 +38,19 @@ public class SuppressionFieldAbility extends Ability{ protected float timer; + @Override + public void init(UnitType type){ + if(!active) display = false; + } + + @Override + public void addStats(Table t){ + super.addStats(t); + t.add(Core.bundle.format("bullet.range", Strings.autoFixed(range / tilesize, 2))); + t.row(); + t.add(abilityStat("duration", Strings.autoFixed(reload / 60f, 2))); + } + @Override public void update(Unit unit){ if(!active) return; diff --git a/core/src/mindustry/entities/abilities/UnitSpawnAbility.java b/core/src/mindustry/entities/abilities/UnitSpawnAbility.java index f4e0f5ecb0..f7ab3667f4 100644 --- a/core/src/mindustry/entities/abilities/UnitSpawnAbility.java +++ b/core/src/mindustry/entities/abilities/UnitSpawnAbility.java @@ -12,7 +12,6 @@ import mindustry.game.EventType.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.type.*; -import mindustry.world.meta.*; import static mindustry.Vars.*; @@ -36,9 +35,10 @@ public class UnitSpawnAbility extends Ability{ @Override public void addStats(Table t){ - t.add("[lightgray]" + Stat.buildTime.localized() + ": [white]" + Strings.autoFixed(spawnTime / 60f, 2) + " " + StatUnit.seconds.localized()); + super.addStats(t); + t.add(abilityStat("buildtime", Strings.autoFixed(spawnTime / 60f, 2))); t.row(); - t.add(unit.emoji() + " " + unit.localizedName); + t.add((unit.hasEmoji() ? unit.emoji() : "") + "[stat]" + unit.localizedName); } @Override diff --git a/core/src/mindustry/world/meta/StatValues.java b/core/src/mindustry/world/meta/StatValues.java index 36872d3546..9c548f2836 100644 --- a/core/src/mindustry/world/meta/StatValues.java +++ b/core/src/mindustry/world/meta/StatValues.java @@ -374,17 +374,23 @@ public class StatValues{ public static StatValue abilities(Seq abilities){ return table -> { table.row(); - table.table(t -> abilities.each(ability -> { - if(ability.display){ - t.row(); - t.table(Styles.grayPanel, a -> { - a.add("[accent]" + ability.localized()).padBottom(4); - a.row(); - a.left().top().defaults().left(); - ability.addStats(a); - }).pad(5).margin(10).growX(); - } - })); + table.table(t -> { + int count = 0; + for(Ability ability : abilities){ + if(ability.display){ + t.table(Styles.grayPanel, a -> { + a.add("[accent]" + ability.localized()).padBottom(4).center().top().expandX(); + a.row(); + a.left().top().defaults().left(); + ability.addStats(a); + }).pad(5).margin(10).growX().top().uniformX(); + if((++count) == 2){ + count = 0; + t.row(); + } + } + }; + }); }; } @@ -496,7 +502,7 @@ public class StatValues{ } if(type.status != StatusEffects.none){ - sep(bt, (type.status.minfo.mod == null ? type.status.emoji() : "") + "[stat]" + type.status.localizedName + (type.status.reactive ? "" : "[lightgray] ~ [stat]" + ((int)(type.statusDuration / 60f)) + "[lightgray] " + Core.bundle.get("unit.seconds"))); + sep(bt, (type.status.hasEmoji() ? type.status.emoji() : "") + "[stat]" + type.status.localizedName + (type.status.reactive ? "" : "[lightgray] ~ [stat]" + ((int)(type.statusDuration / 60f)) + "[lightgray] " + Core.bundle.get("unit.seconds"))); } if(type.intervalBullet != null){ @@ -554,4 +560,4 @@ public class StatValues{ private static TextureRegion icon(UnlockableContent t){ return t.uiIcon; } -} \ No newline at end of file +} From 304d7d9602aefa3df91b4cf1fd81d69fff142644 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Sat, 30 Mar 2024 04:12:20 +0000 Subject: [PATCH 050/348] Automatic bundle update --- core/assets/bundles/bundle_be.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_bg.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_ca.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_cs.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_da.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_de.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_es.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_et.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_eu.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_fi.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_fil.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_fr.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_hu.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_id_ID.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_it.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_ja.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_ko.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_lt.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_nl.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_nl_BE.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_pl.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_pt_BR.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_pt_PT.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_ro.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_ru.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_sr.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_sv.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_th.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_tk.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_tr.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_uk_UA.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_vi.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_zh_CN.properties | 34 +++++++++++++++++++-- core/assets/bundles/bundle_zh_TW.properties | 34 +++++++++++++++++++-- 34 files changed, 1088 insertions(+), 68 deletions(-) diff --git a/core/assets/bundles/bundle_be.properties b/core/assets/bundles/bundle_be.properties index e3a469caac..1ff2a88c87 100644 --- a/core/assets/bundles/bundle_be.properties +++ b/core/assets/bundles/bundle_be.properties @@ -971,17 +971,46 @@ stat.immunities = Імунітэт stat.healing = Аднаўленне ability.forcefield = Сіловое Поле +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Поле Рамонту +ability.repairfield.description = Repairs nearby units ability.statusfield = Поле Статусу +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Завод +ability.unitspawn.description = Constructs units ability.shieldregenfield = Васстанўляюяае Поле Шчыта +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Рух Маланкі +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Шчытавая Дуга +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Энэргетычнае Поле -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Даступны Толькі Перанос Рэсурсаў У Ядро bar.drilltierreq = Патрабуецца свідар лепей @@ -1283,6 +1312,7 @@ rules.unitdamagemultiplier = Множнік страт баяв. адз. rules.unitcrashdamagemultiplier = Множнік Падрыўнога Пашкоджання Юніта rules.solarmultiplier = Множнік Сонечнай Энергіі rules.unitcapvariable = Ядра Спрыяюць Колькасці Юнітаў +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Асноўная Колькасць Юнітаў rules.limitarea = Абмежаваць Вобласць Мапы rules.enemycorebuildradius = Радыус абароны варожае. ядраў: [lightgray] (блок.) diff --git a/core/assets/bundles/bundle_bg.properties b/core/assets/bundles/bundle_bg.properties index bd83830718..ff6ddd795c 100644 --- a/core/assets/bundles/bundle_bg.properties +++ b/core/assets/bundles/bundle_bg.properties @@ -981,17 +981,46 @@ stat.immunities = Immunities stat.healing = Healing ability.forcefield = Енергийно Поле +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Възстановяващо Поле +ability.repairfield.description = Repairs nearby units ability.statusfield = Подсилващо Поле +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Factory +ability.unitspawn.description = Constructs units ability.shieldregenfield = Възстановяващо броня Поле +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Подвижна светкавица +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Shield Arc +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energy Field -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Only Core Depositing Allowed @@ -1294,6 +1323,7 @@ rules.unitdamagemultiplier = Множител на Щетите на Едини rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Solar Power Multiplier rules.unitcapvariable = Ядрата Увеличават Максималния Брой Единици +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Максимален Брой Единици rules.limitarea = Limit Map Area rules.enemycorebuildradius = Радиус на Защитена от Строене Зона Около Ядрата:[lightgray] (полета) diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index 2e2b9b9827..a889b32ffb 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -985,17 +985,46 @@ stat.immunities = Immunitats stat.healing = Reparador ability.forcefield = Camp de força +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Repara el camp de força +ability.repairfield.description = Repairs nearby units ability.statusfield = Estat del camp +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Fàbrica +ability.unitspawn.description = Constructs units ability.shieldregenfield = Regenerador de camps de força +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Moviment llampec +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Escut de descàrregues +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Camp de força -ability.energyfield.sametypehealmultiplier = [lightgray]Mateix tipus de guarició: [white]{0} % -ability.energyfield.maxtargets = [lightgray]Objectius màx.: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneració +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Només es permet depositar al nucli. bar.drilltierreq = Cal una perforadora millor. @@ -1297,6 +1326,7 @@ rules.unitdamagemultiplier = Multiplicador del dany de les unitats rules.unitcrashdamagemultiplier = Multiplicador del dany de xoc de les unitats rules.solarmultiplier = Multiplicador de l’energia solar rules.unitcapvariable = Els nuclis contribueixen al límit d’unitats +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Capacitat base d’unitats rules.limitarea = Limita l’àrea del mapa rules.enemycorebuildradius = Radi de no construcció del nucli enemic:[lightgray] (caselles) diff --git a/core/assets/bundles/bundle_cs.properties b/core/assets/bundles/bundle_cs.properties index cdd5a9fdae..34ac915c51 100644 --- a/core/assets/bundles/bundle_cs.properties +++ b/core/assets/bundles/bundle_cs.properties @@ -983,17 +983,46 @@ stat.immunities = Imunity stat.healing = Léčí se ability.forcefield = Silové pole +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Opravit pole +ability.repairfield.description = Repairs nearby units ability.statusfield = Stav pole +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = továrna +ability.unitspawn.description = Constructs units ability.shieldregenfield = Silově opravné pole +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Pohybující se blesk +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Štítovy Oblouk +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energetické pole -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Pouze Ukládání do Jádra je povoleno @@ -1296,6 +1325,7 @@ rules.unitdamagemultiplier = Násobek poškození jednotkami rules.unitcrashdamagemultiplier = Násobek poškození při nárazu jednotky rules.solarmultiplier = Násobek Solární Energie rules.unitcapvariable = Jádra Zvýšujou Maximum Počtu Jednotek +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Základní Maximum Počtu Jednotek rules.limitarea = Limit Map Area rules.enemycorebuildradius = Poloměr, ve kterém se okolo nepřátelského jádra nesmí stavět: [lightgray](dlaždic)[] diff --git a/core/assets/bundles/bundle_da.properties b/core/assets/bundles/bundle_da.properties index 1e23ad4055..f0bf3f405b 100644 --- a/core/assets/bundles/bundle_da.properties +++ b/core/assets/bundles/bundle_da.properties @@ -972,17 +972,46 @@ stat.immunities = Immunities stat.healing = Healing ability.forcefield = Kraftfelt +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Reparationsfelt +ability.repairfield.description = Repairs nearby units ability.statusfield = Statusfelt +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Fabrik +ability.unitspawn.description = Constructs units ability.shieldregenfield = Skjold-regenereringsfelt +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Movement Lightning +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Shield Arc +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energy Field -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Only Core Depositing Allowed @@ -1285,6 +1314,7 @@ rules.unitdamagemultiplier = Enheds-skade-forstærker rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Solar Power Multiplier rules.unitcapvariable = Cores Contribute To Unit Cap +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Base Unit Cap rules.limitarea = Limit Map Area rules.enemycorebuildradius = Radius af fjendtlig kernes ubebyggelig zone:[lightgray] (felter) diff --git a/core/assets/bundles/bundle_de.properties b/core/assets/bundles/bundle_de.properties index 04488a8786..7ec71b9203 100644 --- a/core/assets/bundles/bundle_de.properties +++ b/core/assets/bundles/bundle_de.properties @@ -994,17 +994,46 @@ stat.immunities = Immunitäten stat.healing = Heilung ability.forcefield = Kraftfeld +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Heilungsfeld +ability.repairfield.description = Repairs nearby units ability.statusfield = Statusfeld +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Fabrik +ability.unitspawn.description = Constructs units ability.shieldregenfield = Schildregenerationsfeld +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Bewegungsblitze +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Lichtbogenschild +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Heilungsunterdrückungsfeld +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energiefeld -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Nur Kernablage möglich @@ -1307,6 +1336,7 @@ rules.unitdamagemultiplier = Einheit-Schaden-Multiplikator rules.unitcrashdamagemultiplier = Einheiten-Absturzschaden-Multiplikator rules.solarmultiplier = Solarstrom-Multiplikator rules.unitcapvariable = Kerne zählen zum Einheiten-Limit dazu +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Einheiten-Limit rules.limitarea = Kartenbereich begrenzen rules.enemycorebuildradius = Bauverbot-Radius durch feindlichen Kern:[lightgray] (Kacheln) diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index 18a16ca4d9..e4b1770621 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -991,17 +991,46 @@ stat.immunities = Inmune a stat.healing = Curación ability.forcefield = Área de Escudo +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Área de Reparación +ability.repairfield.description = Repairs nearby units ability.statusfield = Área de Potenciación +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Fábrica +ability.unitspawn.description = Constructs units ability.shieldregenfield = Área de Regeneración de Armaduras +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Movimiento Relámpago +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Sector de Escudo +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Área de Bloqueo de Regeneración +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Campo de Energía -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Sólo se permite depositar en el núcleo bar.drilltierreq = Requiere un taladro mejor @@ -1303,6 +1332,7 @@ rules.unitdamagemultiplier = Multiplicador de daño de unidades rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Multiplicador de energía solar rules.unitcapvariable = Las categorías del núcleo alteran el límite máximo de unidades +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Límite base de unidades rules.limitarea = Limitar área del mapa rules.enemycorebuildradius = Radio de zona anti-construcción del núcleo enemigo:[lightgray] (bloques) diff --git a/core/assets/bundles/bundle_et.properties b/core/assets/bundles/bundle_et.properties index a56915c145..395e330d14 100644 --- a/core/assets/bundles/bundle_et.properties +++ b/core/assets/bundles/bundle_et.properties @@ -972,17 +972,46 @@ stat.immunities = Immunities stat.healing = Healing ability.forcefield = Force Field +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Repair Field +ability.repairfield.description = Repairs nearby units ability.statusfield = Status Field +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Factory +ability.unitspawn.description = Constructs units ability.shieldregenfield = Shield Regen Field +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Movement Lightning +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Shield Arc +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energy Field -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Only Core Depositing Allowed @@ -1285,6 +1314,7 @@ rules.unitdamagemultiplier = Väeüksuste hävitusvõime kordaja rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Solar Power Multiplier rules.unitcapvariable = Cores Contribute To Unit Cap +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Base Unit Cap rules.limitarea = Limit Map Area rules.enemycorebuildradius = Vaenlaste tuumiku ehitistevaba ala raadius:[lightgray] (ühik) diff --git a/core/assets/bundles/bundle_eu.properties b/core/assets/bundles/bundle_eu.properties index 8ce90d62e5..7bc7d6ffa8 100644 --- a/core/assets/bundles/bundle_eu.properties +++ b/core/assets/bundles/bundle_eu.properties @@ -974,17 +974,46 @@ stat.immunities = Immunities stat.healing = Healing ability.forcefield = Force Field +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Repair Field +ability.repairfield.description = Repairs nearby units ability.statusfield = Status Field +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Factory +ability.unitspawn.description = Constructs units ability.shieldregenfield = Shield Regen Field +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Movement Lightning +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Shield Arc +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energy Field -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Only Core Depositing Allowed @@ -1287,6 +1316,7 @@ rules.unitdamagemultiplier = Unitateen kalte-biderkatzailea rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Solar Power Multiplier rules.unitcapvariable = Cores Contribute To Unit Cap +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Base Unit Cap rules.limitarea = Limit Map Area rules.enemycorebuildradius = Etsaien muinaren ez-eraikitze erradioa:[lightgray] (lauzak) diff --git a/core/assets/bundles/bundle_fi.properties b/core/assets/bundles/bundle_fi.properties index 7a31835dde..25767ac142 100644 --- a/core/assets/bundles/bundle_fi.properties +++ b/core/assets/bundles/bundle_fi.properties @@ -971,17 +971,46 @@ stat.immunities = Immuuni stat.healing = Parantuu ability.forcefield = Voimakenttä +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Korjauskenttä +ability.repairfield.description = Repairs nearby units ability.statusfield = Statuskenttä +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Tehdas +ability.unitspawn.description = Constructs units ability.shieldregenfield = Kilvenvahvistuskenttä +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Salamointi liikkuessa +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Kilpikaari +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energiakenttä -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Sijoittaminen sallittua vain ytimeen @@ -1284,6 +1313,7 @@ rules.unitdamagemultiplier = Yksikköjen vahinkokerroin rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Aurinkovoimakerroin rules.unitcapvariable = Ytimet vaikuttavat yksikkörajaan +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Perusyksikköraja rules.limitarea = Rajoita kartan aluetta rules.enemycorebuildradius = Vihollisytimen rakennuksenestosäde:[lightgray] (laattoina) diff --git a/core/assets/bundles/bundle_fil.properties b/core/assets/bundles/bundle_fil.properties index 92bf22e804..d66dbabc0f 100644 --- a/core/assets/bundles/bundle_fil.properties +++ b/core/assets/bundles/bundle_fil.properties @@ -971,17 +971,46 @@ stat.immunities = Immunities stat.healing = Healing ability.forcefield = Force Field +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Repair Field +ability.repairfield.description = Repairs nearby units ability.statusfield = Status Field +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Factory +ability.unitspawn.description = Constructs units ability.shieldregenfield = Shield Regen Field +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Movement Lightning +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Shield Arc +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energy Field -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Only Core Depositing Allowed @@ -1284,6 +1313,7 @@ rules.unitdamagemultiplier = Unit Damage Multiplier rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Solar Power Multiplier rules.unitcapvariable = Cores Contribute To Unit Cap +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Base Unit Cap rules.limitarea = Limit Map Area rules.enemycorebuildradius = Enemy Core No-Build Radius:[lightgray] (tiles) diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index ce9882b068..c44718029a 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -997,17 +997,46 @@ stat.immunities = Immunités stat.healing = Guérison ability.forcefield = Champ de Force +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Champ de Réparation +ability.repairfield.description = Repairs nearby units ability.statusfield = Champ d'Amélioration +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Usine +ability.unitspawn.description = Constructs units ability.shieldregenfield = Champ de régénération de bouclier +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Déplacement éclair +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Arc de Bouclier +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Champ de Suppression de Soins +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Champ d'énergie -ability.energyfield.sametypehealmultiplier = [lightgray]Soins des Unités du Même Type: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Cibles Maximales: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Régénération +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Seul le dépôt de ressources dans le Noyau est autorisé bar.drilltierreq = Meilleure Foreuse Requise @@ -1312,6 +1341,7 @@ rules.unitdamagemultiplier = Multiplicateur de Dégât des Unités rules.unitcrashdamagemultiplier = Multiplicateur de Dégât de chute des Unités rules.solarmultiplier = Multiplicateur de l'Efficacité des Panneaux Solaires rules.unitcapvariable = Les Noyaux contribuent à la limite d'Unités actives +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Limite initiale d'Unités actives rules.limitarea = Limite de la zone de jeu de la Carte rules.enemycorebuildradius = Périmètre Non-Constructible autour du Noyau ennemi :[lightgray] (blocs) diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index 88ac610b4f..bdaf5bbf1e 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -998,17 +998,46 @@ stat.immunities = Immunitások stat.healing = Gyógyulás ability.forcefield = Erőtér +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Javító mező +ability.repairfield.description = Repairs nearby units ability.statusfield = Állapotmező +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Gyár +ability.unitspawn.description = Constructs units ability.shieldregenfield = Pajzsregeneráló mező +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Villámcsapás +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Pajzs ív +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Javítás elnyomása +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energiamező -ability.energyfield.sametypehealmultiplier = [lightgray]Azonos típusú gyógyítás: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Célpontok maximális száma: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneráció +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Csak a támaszpont elhelyezése megengedett bar.drilltierreq = Erősebb fúró szükséges @@ -1313,6 +1342,7 @@ rules.unitdamagemultiplier = Egység sebzésszorzója rules.unitcrashdamagemultiplier = Egység ütközési sebzésszorzója rules.solarmultiplier = Napenergia szorzója rules.unitcapvariable = A támaszpontok befolyásolják a gyártható egységek darabszámát +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Alap egységdarabszám rules.limitarea = Játékterület korlátozása rules.enemycorebuildradius = Ellenséges támaszpont körüli tiltott zóna sugara:[lightgray] (csempe) diff --git a/core/assets/bundles/bundle_id_ID.properties b/core/assets/bundles/bundle_id_ID.properties index 9abf0debce..0446601dd9 100644 --- a/core/assets/bundles/bundle_id_ID.properties +++ b/core/assets/bundles/bundle_id_ID.properties @@ -991,17 +991,46 @@ stat.immunities = Kekebalan stat.healing = Menyembuhkan ability.forcefield = Bidang Kekuatan +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Bidang Perbaikan +ability.repairfield.description = Repairs nearby units ability.statusfield = Bidang Status +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Pabrik +ability.unitspawn.description = Constructs units ability.shieldregenfield = Bidang Regenerasi Perisai +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Pergerakan Petir +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Shield Arc +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Bidang Tenaga -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Hanya Penyetoran Inti yang Diizinkan bar.drilltierreq = Membutuhkan Bor yang Lebih Baik @@ -1303,6 +1332,7 @@ rules.unitdamagemultiplier = Penggandaan Kekuatan Unit rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Penggandaan Tenaga Surya rules.unitcapvariable = Inti Memengaruhi Batas Unit +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Batas Unit Dasar rules.limitarea = Batas Area Peta rules.enemycorebuildradius = Dilarang Membangun Radius Inti Musuh :[lightgray] (blok) diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties index 01262c1e95..00f76cef1f 100644 --- a/core/assets/bundles/bundle_it.properties +++ b/core/assets/bundles/bundle_it.properties @@ -977,17 +977,46 @@ stat.immunities = Immunità stat.healing = Rigenerazione ability.forcefield = Campo di Forza +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Campo Riparativo +ability.repairfield.description = Repairs nearby units ability.statusfield = Campo di Stato +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Fabbrica +ability.unitspawn.description = Constructs units ability.shieldregenfield = Campo di Rigenerazione Scudo +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Movimento Fulminante +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Shield Arc +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Campo energetico -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Concesso solo il deposito al nucleo @@ -1290,6 +1319,7 @@ rules.unitdamagemultiplier = Moltiplicatore Danno Unità rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Moltiplicatore energia solare rules.unitcapvariable = Cores Contribute To Unit Cap +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Base Unit Cap rules.limitarea = Limite dimensioni mappa rules.enemycorebuildradius = Raggio di protezione del Nucleo Nemico dalle costruzioni:[lightgray] (blocchi) diff --git a/core/assets/bundles/bundle_ja.properties b/core/assets/bundles/bundle_ja.properties index 773c80759e..618db72f7a 100644 --- a/core/assets/bundles/bundle_ja.properties +++ b/core/assets/bundles/bundle_ja.properties @@ -983,17 +983,46 @@ stat.immunities = 耐性 stat.healing = 治癒 ability.forcefield = フォースフィールド +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = リペアフィールド +ability.repairfield.description = Repairs nearby units ability.statusfield = ステータスフィールド +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = 生産 +ability.unitspawn.description = Constructs units ability.shieldregenfield = シールドリペアフィールド +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = ムーブメントライトニング +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = シールドアーク +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = リジェネ抑制フィールド +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = エネルギー範囲 -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = コアにのみ搬入できます。 @@ -1296,6 +1325,7 @@ rules.unitdamagemultiplier = ユニットのダメージ倍率 rules.unitcrashdamagemultiplier = ユニットの衝突ダメージ倍率 rules.solarmultiplier = 太陽光の倍率 rules.unitcapvariable = コア数によってユニット上限を変動 +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = 基礎ユニット上限数 rules.limitarea = マップエリアを制限 rules.enemycorebuildradius = 敵コア周辺の建設禁止区域の半径:[lightgray] (タイル) diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index ca902dbb80..1fabbfdbc6 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -983,17 +983,46 @@ stat.immunities = 상태이상 면역 stat.healing = 회복량 ability.forcefield = 보호막 필드 +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = 수리 필드 +ability.repairfield.description = Repairs nearby units ability.statusfield = 상태이상 필드 +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = 공장 +ability.unitspawn.description = Constructs units ability.shieldregenfield = 방어막 복구 필드 +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = 가속 전격 +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = 방어막 아크 +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = 재생성 억제 필드 +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = 에너지 필드 -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = 코어에만 투입할 수 있습니다 bar.drilltierreq = 더 좋은 드릴 필요 @@ -1295,6 +1324,7 @@ rules.unitdamagemultiplier = 기체 피해량 배수 rules.unitcrashdamagemultiplier = 기체 파손 피해량 배수 rules.solarmultiplier = 태양광 전력 배수 rules.unitcapvariable = 코어 기체수 제한 추가 +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = 기본 기체 제한 rules.limitarea = 맵 영역 제한 rules.enemycorebuildradius = 적 코어 건설금지 범위:[lightgray] (타일) diff --git a/core/assets/bundles/bundle_lt.properties b/core/assets/bundles/bundle_lt.properties index 0cf772d249..40904c3e94 100644 --- a/core/assets/bundles/bundle_lt.properties +++ b/core/assets/bundles/bundle_lt.properties @@ -972,17 +972,46 @@ stat.immunities = Immunities stat.healing = Healing ability.forcefield = Force Field +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Repair Field +ability.repairfield.description = Repairs nearby units ability.statusfield = Status Field +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Factory +ability.unitspawn.description = Constructs units ability.shieldregenfield = Shield Regen Field +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Movement Lightning +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Shield Arc +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energy Field -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Only Core Depositing Allowed @@ -1285,6 +1314,7 @@ rules.unitdamagemultiplier = Vienetų Žalos Daugiklis rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Solar Power Multiplier rules.unitcapvariable = Cores Contribute To Unit Cap +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Base Unit Cap rules.limitarea = Limit Map Area rules.enemycorebuildradius = Nestatymo aplink priešų branduolį spindulys:[lightgray] (blokais) diff --git a/core/assets/bundles/bundle_nl.properties b/core/assets/bundles/bundle_nl.properties index fb8fbe5f10..ad980d279a 100644 --- a/core/assets/bundles/bundle_nl.properties +++ b/core/assets/bundles/bundle_nl.properties @@ -984,17 +984,46 @@ stat.immunities = Immuniteiten stat.healing = Genezing ability.forcefield = Krachtveld +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Reparatieveld +ability.repairfield.description = Repairs nearby units ability.statusfield = Statusveld +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Fabriek +ability.unitspawn.description = Constructs units ability.shieldregenfield = Schild Regeneratie Veld +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Beweging Bliksem +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Schild Boog +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regeneratie Onderdrukkingsveld +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energieveld -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Alleen materialen in de Core toegestaan. @@ -1297,6 +1326,7 @@ rules.unitdamagemultiplier = Eenheid Schade Vermenigvuldiger rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Zonne-Energie Vermenigvuldiger rules.unitcapvariable = Cores Dragen Bij Aan Eenheidslimiet +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Bais Eenheidlimiet rules.limitarea = Limiteer Kaart Gebied rules.enemycorebuildradius = Niet-Bouw Bereik Vijandelijke Cores:[lightgray] (tegels) diff --git a/core/assets/bundles/bundle_nl_BE.properties b/core/assets/bundles/bundle_nl_BE.properties index e03d97d06c..baa57101d7 100644 --- a/core/assets/bundles/bundle_nl_BE.properties +++ b/core/assets/bundles/bundle_nl_BE.properties @@ -972,17 +972,46 @@ stat.immunities = Immunities stat.healing = Healing ability.forcefield = Force Field +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Repair Field +ability.repairfield.description = Repairs nearby units ability.statusfield = Status Field +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Factory +ability.unitspawn.description = Constructs units ability.shieldregenfield = Shield Regen Field +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Movement Lightning +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Shield Arc +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energy Field -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Only Core Depositing Allowed @@ -1285,6 +1314,7 @@ rules.unitdamagemultiplier = Unit Damage Multiplier rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Solar Power Multiplier rules.unitcapvariable = Cores Contribute To Unit Cap +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Base Unit Cap rules.limitarea = Limit Map Area rules.enemycorebuildradius = Enemy Core No-Build Radius:[lightgray] (tiles) diff --git a/core/assets/bundles/bundle_pl.properties b/core/assets/bundles/bundle_pl.properties index 5e19b1e4e4..afe941105e 100644 --- a/core/assets/bundles/bundle_pl.properties +++ b/core/assets/bundles/bundle_pl.properties @@ -981,17 +981,46 @@ stat.immunities = Odporności stat.healing = Leczy ability.forcefield = Pole Siłowe +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Pole Naprawy +ability.repairfield.description = Repairs nearby units ability.statusfield = Pole Statusu +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Fabryka Jednostek +ability.unitspawn.description = Constructs units ability.shieldregenfield = Strefa Tarczy Regenerującej +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Pioruny Poruszania +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Łuk Tarczy +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Pole Tłumienia Regeneracji +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Pole Energii -ability.energyfield.sametypehealmultiplier = [lightgray]Ten sam typ leczenia: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Maksymalne cele: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneracja +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Dozwolone jest tylko przeniesienie z rdzenia @@ -1294,6 +1323,7 @@ rules.unitdamagemultiplier = Mnożnik Obrażeń jednostek rules.unitcrashdamagemultiplier = Obrażenia Zadawane Po Zniszczeniu rules.solarmultiplier = Mnożnik Mocy Paneli Słonecznych rules.unitcapvariable = Rdzenie mają wpływ na limit jednostek +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Podstawowy limit jednostek rules.limitarea = Limit Obszaru Mapy rules.enemycorebuildradius = Zasięg Blokady Budowy Przy Rdzeniu Wroga:[lightgray] (kratki) diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index 8c757e81b6..5d854f1e64 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -992,17 +992,46 @@ stat.immunities = Imunidades stat.healing = Reparo ability.forcefield = Campo de Força +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Campo de Reparação +ability.repairfield.description = Repairs nearby units ability.statusfield = Campo de Status +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Fábrica +ability.unitspawn.description = Constructs units ability.shieldregenfield = Raio de Regeneração do Escudo +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Raio de Movimento +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Arco do Escudo +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Campo de Energia -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Somente depósito no núcleo permitido bar.drilltierreq = Broca melhor necessária. @@ -1304,6 +1333,7 @@ rules.unitdamagemultiplier = Multiplicador de dano de Unidade rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Multiplicador de Energia Solar rules.unitcapvariable = Núcleos contribuem para a capacidade da unidade +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Capacidade base da Unidade rules.limitarea = Limitar área do mapa rules.enemycorebuildradius = Raio de "não-criação" de núcleo inimigo:[lightgray] (blocos) diff --git a/core/assets/bundles/bundle_pt_PT.properties b/core/assets/bundles/bundle_pt_PT.properties index f5eb2863fa..0ce81ecf7d 100644 --- a/core/assets/bundles/bundle_pt_PT.properties +++ b/core/assets/bundles/bundle_pt_PT.properties @@ -972,17 +972,46 @@ stat.immunities = Immunities stat.healing = Healing ability.forcefield = Force Field +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Repair Field +ability.repairfield.description = Repairs nearby units ability.statusfield = Status Field +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Factory +ability.unitspawn.description = Constructs units ability.shieldregenfield = Shield Regen Field +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Movement Lightning +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Shield Arc +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energy Field -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Only Core Depositing Allowed @@ -1285,6 +1314,7 @@ rules.unitdamagemultiplier = Multiplicador de dano de Unidade rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Solar Power Multiplier rules.unitcapvariable = Cores Contribute To Unit Cap +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Base Unit Cap rules.limitarea = Limit Map Area rules.enemycorebuildradius = Raio de "Não-criação" de core inimigo:[lightgray] (blocos) diff --git a/core/assets/bundles/bundle_ro.properties b/core/assets/bundles/bundle_ro.properties index eb1d0ce995..da51937357 100644 --- a/core/assets/bundles/bundle_ro.properties +++ b/core/assets/bundles/bundle_ro.properties @@ -983,17 +983,46 @@ stat.immunities = Immunities stat.healing = Reparare ability.forcefield = Câmp de Forță +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Câmp de Reparare +ability.repairfield.description = Repairs nearby units ability.statusfield = Câmp de Stare +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Fabrică +ability.unitspawn.description = Constructs units ability.shieldregenfield = Câmp Regenerare Scut +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Mișcare Fulger +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Shield Arc +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Câmp de Energie -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Only Core Depositing Allowed @@ -1296,6 +1325,7 @@ rules.unitdamagemultiplier = Multiplicatorul Deteriorării Unităților rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Solar Power Multiplier rules.unitcapvariable = Nucleele Contribuie la Limita Unităților +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Limita de Bază a Unităților rules.limitarea = Limit Map Area rules.enemycorebuildradius = Interzisă Construirea în Jurul Nucleului Inamic:[lightgray] (pătrate) diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index 268d96520c..b0b35d7083 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -984,17 +984,46 @@ stat.immunities = Невосприимчив stat.healing = Ремонт ability.forcefield = Силовое поле +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Ремонтирующее поле +ability.repairfield.description = Repairs nearby units ability.statusfield = Усиливающее поле +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Завод единиц � +ability.unitspawn.description = Constructs units ability.shieldregenfield = Поле восстановления щита +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Молнии при движении +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Дуговой щит +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Поле подавления регенерации +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Энергетическое поле -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Доступен перенос только в ядро bar.drilltierreq = Требуется бур получше @@ -1296,6 +1325,7 @@ rules.unitdamagemultiplier = Множитель урона боев. ед. rules.unitcrashdamagemultiplier = Множитель урона от падения боев. ед. rules.solarmultiplier = Множитель солнечной энергии rules.unitcapvariable = Ядра увеличивают лимит единиц +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Начальный лимит единиц rules.limitarea = Ограничить область карты rules.enemycorebuildradius = Радиус защиты враж. ядер:[lightgray] (блок.) diff --git a/core/assets/bundles/bundle_sr.properties b/core/assets/bundles/bundle_sr.properties index 588f323ef7..fac38a433f 100644 --- a/core/assets/bundles/bundle_sr.properties +++ b/core/assets/bundles/bundle_sr.properties @@ -985,17 +985,46 @@ stat.immunities = Imuniteti stat.healing = Popravlja ability.forcefield = Polje Sile +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Polje Popravke +ability.repairfield.description = Repairs nearby units ability.statusfield = Statusno Polje +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Fabrika +ability.unitspawn.description = Constructs units ability.shieldregenfield = Brzina Obnove Štita +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Munje Pri Kretanju +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Elektrolučni Štit +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Polje Prigušivanja Popravki +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energetsko Polje -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Dozvoljeno Dostavljanje Samo Unutar Jezgra @@ -1298,6 +1327,7 @@ rules.unitdamagemultiplier = Unit Damage Multiplier rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Solar Power Multiplier rules.unitcapvariable = Jezgara Povećavaju Maksimalni Broj Jedinica +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Maksimalni Broj Jedinica (ne računajući jezgra) rules.limitarea = Ograniči Prostor Mape rules.enemycorebuildradius = Radius Neprijateljskog jezgra bez gradnje:[lightgray] (polja) diff --git a/core/assets/bundles/bundle_sv.properties b/core/assets/bundles/bundle_sv.properties index d20f0fd841..25e3813f40 100644 --- a/core/assets/bundles/bundle_sv.properties +++ b/core/assets/bundles/bundle_sv.properties @@ -972,17 +972,46 @@ stat.immunities = Immunities stat.healing = Healing ability.forcefield = Force Field +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Repair Field +ability.repairfield.description = Repairs nearby units ability.statusfield = Status Field +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Factory +ability.unitspawn.description = Constructs units ability.shieldregenfield = Shield Regen Field +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Movement Lightning +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Shield Arc +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energy Field -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Only Core Depositing Allowed @@ -1285,6 +1314,7 @@ rules.unitdamagemultiplier = Unit Damage Multiplier rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Solar Power Multiplier rules.unitcapvariable = Cores Contribute To Unit Cap +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Base Unit Cap rules.limitarea = Limit Map Area rules.enemycorebuildradius = Enemy Core No-Build Radius:[lightgray] (tiles) diff --git a/core/assets/bundles/bundle_th.properties b/core/assets/bundles/bundle_th.properties index 56c4c85f16..72356a9518 100644 --- a/core/assets/bundles/bundle_th.properties +++ b/core/assets/bundles/bundle_th.properties @@ -985,17 +985,46 @@ stat.immunities = ต่อต้านสถานะ stat.healing = การรักษา ability.forcefield = โล่พลังงาน +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = สนามซ่อมแซม +ability.repairfield.description = Repairs nearby units ability.statusfield = สนามเอฟเฟกต์ +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = โรงงานผลิต +ability.unitspawn.description = Constructs units ability.shieldregenfield = สนามรักษาโล่ +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = ปล่อยสายฟ้าเมื่อเคลื่อนที่ +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = โล่พลังงานโค้ง +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = สนามระงับการฟื้นฟู +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = สนามพลังงาน -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = ขนย้ายทรัพยากรลงแกนกลางได้เท่านั้น bar.drilltierreq = ต้องมีเครื่องขุดที่ดีกว่านี้ @@ -1297,6 +1326,7 @@ rules.unitdamagemultiplier = พหุคูณพลังโจมตีขอ rules.unitcrashdamagemultiplier = พหูคูณดาเมจการตกของยานยูนิต rules.solarmultiplier = พหูคุณพลังงานแสงอาทิตย์ rules.unitcapvariable = เพิ่มจำนวนยูนิตสูงสุดต่อแกนกลาง +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = ขีดกำจัดยูนิตสูงสุดพื้นฐาน rules.limitarea = จำกัดพื้นที่แมพ rules.enemycorebuildradius = รัศมีห้ามสร้างบริเวณแกนกลางของศัตรู:[lightgray] (ช่อง) diff --git a/core/assets/bundles/bundle_tk.properties b/core/assets/bundles/bundle_tk.properties index 36106ee582..7231208b63 100644 --- a/core/assets/bundles/bundle_tk.properties +++ b/core/assets/bundles/bundle_tk.properties @@ -972,17 +972,46 @@ stat.immunities = Immunities stat.healing = Healing ability.forcefield = Force Field +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Repair Field +ability.repairfield.description = Repairs nearby units ability.statusfield = Status Field +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Factory +ability.unitspawn.description = Constructs units ability.shieldregenfield = Shield Regen Field +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Movement Lightning +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Shield Arc +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Energy Field -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Only Core Depositing Allowed @@ -1285,6 +1314,7 @@ rules.unitdamagemultiplier = Unit Damage Multiplier rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = Solar Power Multiplier rules.unitcapvariable = Cores Contribute To Unit Cap +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Base Unit Cap rules.limitarea = Limit Map Area rules.enemycorebuildradius = Enemy Core No-Build Radius:[lightgray] (tiles) diff --git a/core/assets/bundles/bundle_tr.properties b/core/assets/bundles/bundle_tr.properties index 9eca8e0bea..e7c192a09e 100644 --- a/core/assets/bundles/bundle_tr.properties +++ b/core/assets/bundles/bundle_tr.properties @@ -982,17 +982,46 @@ stat.immunities = Bağışıklıklar stat.healing = Tamir Eder ability.forcefield = Güç Kalkanı +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Onarma Alanı +ability.repairfield.description = Repairs nearby units ability.statusfield = Hızlandırma Alanı +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Birliği Fabrikası +ability.unitspawn.description = Constructs units ability.shieldregenfield = Kalkan Yenileme Alanı +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Hareket Enerjisi +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Ark Kalkanı +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Tamir Engelleme Alanı +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Güç Kalkanı -ability.energyfield.sametypehealmultiplier = [lightgray]Aynı Türden İyileştirme: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Azami Hedefler: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Yenilenme +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Sadece Merkeze Aktarım Mümkün bar.drilltierreq = Daha Güçlü Matkap Gerekli @@ -1294,6 +1323,7 @@ rules.unitdamagemultiplier = Birim Hasar Çapanı rules.unitcrashdamagemultiplier = Birim Çakılma Hasar Çarpanı rules.solarmultiplier = Güneş Paneli Üretim Çarpanı rules.unitcapvariable = Merkezler Birim Sınırını Etkiler +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Sabit Birim Sınırı rules.limitarea = Haritayı Sınırla rules.enemycorebuildradius = Düşman Merkezi İnşa Yasağı Yarıçapı: [lightgray](kare) diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index a6b59822ba..f640333cb5 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -993,17 +993,46 @@ stat.immunities = Імунітети stat.healing = Відновлювання ability.forcefield = Щитове поле +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Ремонтувальне поле +ability.repairfield.description = Repairs nearby units ability.statusfield = Поле підсилення +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Завод одиниць � +ability.unitspawn.description = Constructs units ability.shieldregenfield = Щитовідновлювальне поле +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Блискавки під час руху +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Щитова дуга +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Поле пригнічення відновлення +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Енергетичне поле -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Передача предметів дозволена лише до ядра bar.drilltierreq = Потрібен ліпший бур @@ -1305,6 +1334,7 @@ rules.unitdamagemultiplier = Множник шкоди бойових одини rules.unitcrashdamagemultiplier = Множник шкоди одиниці при зіткненні одиниць rules.solarmultiplier = Множник сонячної енергії rules.unitcapvariable = Ядра збільшують обмеження на кількість одиниць +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Початкове обмеження одиниць rules.limitarea = Обмежити територію мапи rules.enemycorebuildradius = Радіус оборони для ворожого ядра:[lightgray] (плитки) diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index a8c7a612ae..cb1a9d8886 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -986,17 +986,46 @@ stat.immunities = Miễn nhiễm stat.healing = Sửa chữa ability.forcefield = Tạo khiên +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Sửa chữa/Xây dựng +ability.repairfield.description = Repairs nearby units ability.statusfield = Vùng gia tốc +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = Sản xuất +ability.unitspawn.description = Constructs units ability.shieldregenfield = Tạo khiên nhỏ +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = Phóng điện khi di chuyển +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Khiên Vòng Cung +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Trường sửa chữa +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = Trường điện từ -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = Chỉ có thể đưa vào căn cứ bar.drilltierreq = Cần máy khoan tốt hơn @@ -1298,6 +1327,7 @@ rules.unitdamagemultiplier = Hệ số sát thương của đơn vị rules.unitcrashdamagemultiplier = Hệ số sát thương của đơn vị khi bị bắn rơi rules.solarmultiplier = Hệ số năng lượng mặt trời rules.unitcapvariable = Căn cứ tăng giới hạn đơn vị +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Giới hạn đơn vị rules.limitarea = Giới hạn kích thước bản đồ rules.enemycorebuildradius = Bán kính không xây dựng trong căn cứ của kẻ địch:[lightgray] (ô) diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index 4b4b1ab1ae..0337770822 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -994,17 +994,46 @@ stat.immunities = 免疫 stat.healing = 治疗 ability.forcefield = 力墙场 +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = 修复场 +ability.repairfield.description = Repairs nearby units ability.statusfield = 状态场 +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = 单位工厂 +ability.unitspawn.description = Constructs units ability.shieldregenfield = 护盾再生场 +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = 闪电助推器 +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = 弧形护盾 +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = 修复压制场 +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = 能量场: -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = 仅核心可丢入资源 bar.drilltierreq = 需要更高级的钻头 @@ -1306,6 +1335,7 @@ rules.unitdamagemultiplier = 单位伤害倍率 rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = 太阳能发电倍率 rules.unitcapvariable = 核心可增加单位上限 +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = 基础单位上限 rules.limitarea = 限制地图有效区域 rules.enemycorebuildradius = 敌方核心不可建造区域半径:[lightgray](格) diff --git a/core/assets/bundles/bundle_zh_TW.properties b/core/assets/bundles/bundle_zh_TW.properties index 93e43f4741..7fcd78b424 100644 --- a/core/assets/bundles/bundle_zh_TW.properties +++ b/core/assets/bundles/bundle_zh_TW.properties @@ -990,17 +990,46 @@ stat.immunities = Immunities stat.healing = 治癒 ability.forcefield = 防護罩 +ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = 維修力場 +ability.repairfield.description = Repairs nearby units ability.statusfield = 狀態力場 +ability.statusfield.description = Applies a status effect to nearby units ability.unitspawn = 工廠 +ability.unitspawn.description = Constructs units ability.shieldregenfield = 護盾充能力場 +ability.shieldregenfield.description = Regenerates shields of nearby units ability.movelightning = 移動閃電 +ability.movelightning.description = Releases lightning while moving +ability.armorplate = Armor Plate +ability.armorplate.description = Reduces damage taken while shooting ability.shieldarc = Shield Arc +ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets ability.suppressionfield = Regen Suppression Field +ability.suppressionfield.description = Stops nearby repair buildings ability.energyfield = 能量場: -ability.energyfield.sametypehealmultiplier = [lightgray]Same Type Healing: [white]{0}% -ability.energyfield.maxtargets = [lightgray]Max Targets: [white]{0} +ability.energyfield.description = Zaps nearby enemies +ability.energyfield.healdescription = Zaps nearby enemies and heals allies ability.regen = Regeneration +ability.regen.description = Regenerates own health over time +ability.liquidregen = Liquid Absorption +ability.liquidregen.description = Absorbs liquid to heal itself +ability.spawndeath = Death Spawns +ability.spawndeath.description = Releases units on death +ability.liquidexplode = Death Spillage +ability.liquidexplode.description = Spills liquid on death +ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate +ability.stat.regen = [stat]{0}[lightgray] health/sec +ability.stat.shield = [stat]{0}[lightgray] shield +ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed +ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit +ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown +ability.stat.maxtargets = [stat]{0}[lightgray] max targets +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount +ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction +ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed +ability.stat.duration = [stat]{0} sec[lightgray] duration +ability.stat.buildtime = [stat]{0} sec[lightgray] build time bar.onlycoredeposit = 僅允許向核心放置物品 bar.drilltierreq = 需要更好的鑽頭 @@ -1302,6 +1331,7 @@ rules.unitdamagemultiplier = 單位傷害加成 rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.solarmultiplier = 太陽能電加成 rules.unitcapvariable = 核心限制單位上限 +rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = 基礎單位上限 rules.limitarea = 限制地圖區域 rules.enemycorebuildradius = 敵人核心禁止建設半徑︰[lightgray](格) From d4abcc91efb981411a736c6a658268645e6d0c4d Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 30 Mar 2024 09:53:08 -0400 Subject: [PATCH 051/348] Removed unit formation minSpeed --- core/src/mindustry/ai/UnitGroup.java | 20 -------------------- core/src/mindustry/ai/types/CommandAI.java | 9 --------- core/src/mindustry/input/InputHandler.java | 8 -------- gradle.properties | 2 +- 4 files changed, 1 insertion(+), 38 deletions(-) diff --git a/core/src/mindustry/ai/UnitGroup.java b/core/src/mindustry/ai/UnitGroup.java index 2721c07d55..c712885846 100644 --- a/core/src/mindustry/ai/UnitGroup.java +++ b/core/src/mindustry/ai/UnitGroup.java @@ -12,30 +12,12 @@ import mindustry.async.*; import mindustry.content.*; import mindustry.core.*; import mindustry.gen.*; -import mindustry.world.blocks.environment.*; public class UnitGroup{ public Seq units = new Seq<>(); public int collisionLayer; public volatile float[] positions, originalPositions; public volatile boolean valid; - public long lastSpeedUpdate = -1; - public float minSpeed = 999999f; - - public void updateMinSpeed(){ - if(lastSpeedUpdate == Vars.state.updateId) return; - - lastSpeedUpdate = Vars.state.updateId; - minSpeed = 999999f; - - for(Unit unit : units){ - //don't factor in the floor speed multiplier - Floor on = unit.isFlying() ? Blocks.air.asFloor() : unit.floorOn(); - minSpeed = Math.min(unit.speed() / on.speedMultiplier, minSpeed); - } - - if(Float.isInfinite(minSpeed) || Float.isNaN(minSpeed)) minSpeed = 999999f; - } public void calculateFormation(Vec2 dest, int collisionLayer){ this.collisionLayer = collisionLayer; @@ -58,8 +40,6 @@ public class UnitGroup{ unit.command().groupIndex = i; } - updateMinSpeed(); - //run on new thread to prevent stutter Vars.mainExecutor.submit(() -> { //unused space between circles that needs to be reached for compression to end diff --git a/core/src/mindustry/ai/types/CommandAI.java b/core/src/mindustry/ai/types/CommandAI.java index 692aff98c8..1fc6a83a8d 100644 --- a/core/src/mindustry/ai/types/CommandAI.java +++ b/core/src/mindustry/ai/types/CommandAI.java @@ -148,10 +148,6 @@ public class CommandAI extends AIController{ } } - if(group != null){ - group.updateMinSpeed(); - } - if(!net.client() && command == UnitCommand.enterPayloadCommand && unit.buildOn() != null && (targetPos == null || (world.buildWorld(targetPos.x, targetPos.y) != null && world.buildWorld(targetPos.x, targetPos.y) == unit.buildOn()))){ var build = unit.buildOn(); tmpPayload.unit = unit; @@ -363,11 +359,6 @@ public class CommandAI extends AIController{ } } - @Override - public float prefSpeed(){ - return group == null ? super.prefSpeed() : Math.min(group.minSpeed, unit.speed()); - } - @Override public boolean shouldFire(){ return stance != UnitStance.holdFire; diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index 77fa4e668c..e4ba051bac 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -311,18 +311,10 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ } } - float minSpeed = 100000000f; for(int i = 0; i < groups.length; i ++){ var group = groups[i]; if(group != null && group.units.size > 0){ group.calculateFormation(targetAsVec, i); - minSpeed = Math.min(group.minSpeed, minSpeed); - } - } - - for(var group : groups){ - if(group != null){ - group.minSpeed = minSpeed; } } } diff --git a/gradle.properties b/gradle.properties index 50034acf47..1733b891ba 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=8d5651a6ad +archash=e312cc96f5 From a8588c38b404d9067148efa176aa792bffafb8cd Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 30 Mar 2024 14:22:22 -0400 Subject: [PATCH 052/348] Fixed #9687 --- core/assets/bundles/bundle.properties | 2 +- core/src/mindustry/logic/LStatements.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 94d1e5d086..239a70034a 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -2346,7 +2346,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Spawn unit at a location. lst.applystatus = Apply or clear a status effect from a unit. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Spawn a wave. lst.explosion = Create an explosion at a location. diff --git a/core/src/mindustry/logic/LStatements.java b/core/src/mindustry/logic/LStatements.java index e04293a8e7..29dde175b5 100644 --- a/core/src/mindustry/logic/LStatements.java +++ b/core/src/mindustry/logic/LStatements.java @@ -1380,7 +1380,7 @@ public class LStatements{ } } - @RegisterStatement("weathersensor") + @RegisterStatement("weathersense") public static class WeatherSenseStatement extends LStatement{ public String to = "result"; public String weather = "@rain"; From ba1cacb55f6042cbe1fc73476808fa74ce713e95 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Sat, 30 Mar 2024 18:23:20 +0000 Subject: [PATCH 053/348] Automatic bundle update --- core/assets/bundles/bundle_be.properties | 2 +- core/assets/bundles/bundle_bg.properties | 2 +- core/assets/bundles/bundle_ca.properties | 2 +- core/assets/bundles/bundle_cs.properties | 2 +- core/assets/bundles/bundle_da.properties | 2 +- core/assets/bundles/bundle_de.properties | 2 +- core/assets/bundles/bundle_es.properties | 2 +- core/assets/bundles/bundle_et.properties | 2 +- core/assets/bundles/bundle_eu.properties | 2 +- core/assets/bundles/bundle_fi.properties | 2 +- core/assets/bundles/bundle_fil.properties | 2 +- core/assets/bundles/bundle_fr.properties | 2 +- core/assets/bundles/bundle_hu.properties | 2 +- core/assets/bundles/bundle_id_ID.properties | 2 +- core/assets/bundles/bundle_it.properties | 2 +- core/assets/bundles/bundle_ja.properties | 2 +- core/assets/bundles/bundle_ko.properties | 2 +- core/assets/bundles/bundle_lt.properties | 2 +- core/assets/bundles/bundle_nl.properties | 2 +- core/assets/bundles/bundle_nl_BE.properties | 2 +- core/assets/bundles/bundle_pl.properties | 2 +- core/assets/bundles/bundle_pt_BR.properties | 2 +- core/assets/bundles/bundle_pt_PT.properties | 2 +- core/assets/bundles/bundle_ro.properties | 2 +- core/assets/bundles/bundle_ru.properties | 2 +- core/assets/bundles/bundle_sr.properties | 2 +- core/assets/bundles/bundle_sv.properties | 2 +- core/assets/bundles/bundle_th.properties | 2 +- core/assets/bundles/bundle_tk.properties | 2 +- core/assets/bundles/bundle_tr.properties | 2 +- core/assets/bundles/bundle_uk_UA.properties | 2 +- core/assets/bundles/bundle_vi.properties | 2 +- core/assets/bundles/bundle_zh_CN.properties | 2 +- core/assets/bundles/bundle_zh_TW.properties | 2 +- 34 files changed, 34 insertions(+), 34 deletions(-) diff --git a/core/assets/bundles/bundle_be.properties b/core/assets/bundles/bundle_be.properties index 1ff2a88c87..28be5a9d62 100644 --- a/core/assets/bundles/bundle_be.properties +++ b/core/assets/bundles/bundle_be.properties @@ -2294,7 +2294,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Spawn unit at a location. lst.applystatus = Apply or clear a status effect from a uniut. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulate a wave being spawned at a arbitrary location.\nWill not increment the wave counter. lst.explosion = Create an explosion at a location. diff --git a/core/assets/bundles/bundle_bg.properties b/core/assets/bundles/bundle_bg.properties index ff6ddd795c..7c6656a87e 100644 --- a/core/assets/bundles/bundle_bg.properties +++ b/core/assets/bundles/bundle_bg.properties @@ -2308,7 +2308,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Spawn unit at a location. lst.applystatus = Apply or clear a status effect from a uniut. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulate a wave being spawned at a arbitrary location.\nWill not increment the wave counter. lst.explosion = Create an explosion at a location. diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index a889b32ffb..6e373a2606 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -2318,7 +2318,7 @@ lst.getblock = Obtén les dades d’un bloc en qualsevol posició. lst.setblock = Estableix les dades d’un bloc en qualsevol posició. lst.spawnunit = Fes aparèixer una unitat en una posició. lst.applystatus = Aplica o esborra un efecte d’estat d’una unitat. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simula l’aparició d’una onada enemiga en una posició arbitrària.\nEl comptador d’onades no s’incrementarà. lst.explosion = Crea una explosió en una posició. diff --git a/core/assets/bundles/bundle_cs.properties b/core/assets/bundles/bundle_cs.properties index 34ac915c51..a44a8cea4c 100644 --- a/core/assets/bundles/bundle_cs.properties +++ b/core/assets/bundles/bundle_cs.properties @@ -2313,7 +2313,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Spawn unit at a location. lst.applystatus = Apply or clear a status effect from a uniut. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulate a wave being spawned at a arbitrary location.\nWill not increment the wave counter. lst.explosion = Create an explosion at a location. diff --git a/core/assets/bundles/bundle_da.properties b/core/assets/bundles/bundle_da.properties index f0bf3f405b..ba68c6cb22 100644 --- a/core/assets/bundles/bundle_da.properties +++ b/core/assets/bundles/bundle_da.properties @@ -2294,7 +2294,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Spawn unit at a location. lst.applystatus = Apply or clear a status effect from a uniut. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulate a wave being spawned at a arbitrary location.\nWill not increment the wave counter. lst.explosion = Create an explosion at a location. diff --git a/core/assets/bundles/bundle_de.properties b/core/assets/bundles/bundle_de.properties index 7ec71b9203..ad2c09c8f7 100644 --- a/core/assets/bundles/bundle_de.properties +++ b/core/assets/bundles/bundle_de.properties @@ -2343,7 +2343,7 @@ lst.getblock = Lese Tile-Daten von jedem Standort. lst.setblock = Setze Tile-Daten an jedem Standort. lst.spawnunit = Einheit an einem Standort erstellen. lst.applystatus = Füge einer Einheit einen Effekt hinzu oder entferne ihn. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Schickt die nächste Welle. lst.explosion = Erstellt an einer beliebigen Stelle eine Explosion. diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index e4b1770621..37aaf8fa5c 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -2336,7 +2336,7 @@ lst.getblock = Obtiene los datos de un bloque en cualquier lugar. lst.setblock = Cambia los datos de un bloque en cualquier lugar. lst.spawnunit = Crea una unidad en una localización. lst.applystatus = Aplica o elimina un efecto de alteración de estado a una unidad. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simula la aparición de una oleada de enemigos en una localización arbitraria.\nNo incrementará el contador de oleadas. lst.explosion = Crea una explosión en una localización. diff --git a/core/assets/bundles/bundle_et.properties b/core/assets/bundles/bundle_et.properties index 395e330d14..cf46c49acf 100644 --- a/core/assets/bundles/bundle_et.properties +++ b/core/assets/bundles/bundle_et.properties @@ -2296,7 +2296,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Spawn unit at a location. lst.applystatus = Apply or clear a status effect from a uniut. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulate a wave being spawned at a arbitrary location.\nWill not increment the wave counter. lst.explosion = Create an explosion at a location. diff --git a/core/assets/bundles/bundle_eu.properties b/core/assets/bundles/bundle_eu.properties index 7bc7d6ffa8..f97845fc3e 100644 --- a/core/assets/bundles/bundle_eu.properties +++ b/core/assets/bundles/bundle_eu.properties @@ -2298,7 +2298,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Spawn unit at a location. lst.applystatus = Apply or clear a status effect from a uniut. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulate a wave being spawned at a arbitrary location.\nWill not increment the wave counter. lst.explosion = Create an explosion at a location. diff --git a/core/assets/bundles/bundle_fi.properties b/core/assets/bundles/bundle_fi.properties index 25767ac142..011cb44d22 100644 --- a/core/assets/bundles/bundle_fi.properties +++ b/core/assets/bundles/bundle_fi.properties @@ -2299,7 +2299,7 @@ lst.getblock = Selvitä laattadata missä tahansa sijainnissa. lst.setblock = Aseta laattadata missä tahansa sijainnissa. lst.spawnunit = Luo joukko tietyssä sijainnissa. lst.applystatus = Lisää tai poista statusefekti yksiköltä. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simuloi tason syntymistä mielivaltaisessa sijainnissa.\nEi vaikuta tasolaskuriin. lst.explosion = Luo räjähdys tietyssä sijainnissa. diff --git a/core/assets/bundles/bundle_fil.properties b/core/assets/bundles/bundle_fil.properties index d66dbabc0f..92634c5367 100644 --- a/core/assets/bundles/bundle_fil.properties +++ b/core/assets/bundles/bundle_fil.properties @@ -2295,7 +2295,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Spawn unit at a location. lst.applystatus = Apply or clear a status effect from a uniut. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulate a wave being spawned at a arbitrary location.\nWill not increment the wave counter. lst.explosion = Create an explosion at a location. diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index c44718029a..d044c45163 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -2344,7 +2344,7 @@ lst.getblock = Obtient les données d'une tuile à n'importe quel emplacement. lst.setblock = Définit les données d'une tuile à n'importe quel emplacement. lst.spawnunit = Fait apparaître une unité à un emplacement. lst.applystatus = Ajoute ou enlève un effet de statut d'une unité. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simule un déclenchement de vague à n'importe quel emplacement.\nCela n'incrémente pas le compteur de vaugues. lst.explosion = Crée une explosion à un emplacement. diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index bdaf5bbf1e..bb618d854c 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -2345,7 +2345,7 @@ lst.getblock = Csempeadatok lekérdezése tetszőleges helyen. lst.setblock = Csempeadatok beállítása tetszőleges helyen. lst.spawnunit = Egység lerakása az adott helyen. lst.applystatus = Állapothatás alkalmazása vagy törlése egy egységről. -lst.weathersensor = Ellenőrzés, hogy egy adott időjárástípus aktív-e. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Az időjárástípus jelenlegi állapotának megadása. lst.spawnwave = Egy hullám indítása. lst.explosion = Robbanás létrehozása az adott helyen. diff --git a/core/assets/bundles/bundle_id_ID.properties b/core/assets/bundles/bundle_id_ID.properties index 0446601dd9..e9040f5bab 100644 --- a/core/assets/bundles/bundle_id_ID.properties +++ b/core/assets/bundles/bundle_id_ID.properties @@ -2334,7 +2334,7 @@ lst.getblock = Mendapatkan data petak di lokasi manapun. lst.setblock = Menentukan data petak di lokasi manapun. lst.spawnunit = Munculkan unit pada tempat yang ditentukan. lst.applystatus = Menerapkan atau menghapus status efek dari sebuah unit. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulasikan adanya gelombang pada lokasi acak.\nTidak akan ditambahkan pada jumlah gelombang keseluruhan. lst.explosion = Membuat sebuah ledakan pada lokasi yang ditentukan. diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties index 00f76cef1f..1db134fcd2 100644 --- a/core/assets/bundles/bundle_it.properties +++ b/core/assets/bundles/bundle_it.properties @@ -2308,7 +2308,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Spawn unit at a location. lst.applystatus = Apply or clear a status effect from a uniut. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulate a wave being spawned at a arbitrary location.\nWill not increment the wave counter. lst.explosion = Create an explosion at a location. diff --git a/core/assets/bundles/bundle_ja.properties b/core/assets/bundles/bundle_ja.properties index 618db72f7a..d67268871a 100644 --- a/core/assets/bundles/bundle_ja.properties +++ b/core/assets/bundles/bundle_ja.properties @@ -2312,7 +2312,7 @@ lst.getblock = 任意の座標のタイルの情報を取得します。 lst.setblock = 任意の座標のタイルの情報を変更します。 lst.spawnunit = 任意の座標にユニットをスポーンさせます。 lst.applystatus = ユニットからステータス効果を適用または削除する。 -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = 任意の座標で発生するウェーブをシミュレーションします。\nウェーブを進めません。 lst.explosion = ある場所で爆発を起こします。 diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index 1fabbfdbc6..8cdb1beacf 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -2311,7 +2311,7 @@ lst.getblock = 특정 위치의 타일 정보를 불러옴 lst.setblock = 특정 위치의 타일 정보 설정 lst.spawnunit = 특정 위치에 기체 소환 lst.applystatus = 기체에게 상태이상을 적용하거나 삭제 -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = 특정 위치에 이전 단계를 실행\n실제 단계가 넘어가지 않습니다 lst.explosion = 특정 위치에 폭발 생성 diff --git a/core/assets/bundles/bundle_lt.properties b/core/assets/bundles/bundle_lt.properties index 40904c3e94..210ccafb06 100644 --- a/core/assets/bundles/bundle_lt.properties +++ b/core/assets/bundles/bundle_lt.properties @@ -2296,7 +2296,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Spawn unit at a location. lst.applystatus = Apply or clear a status effect from a uniut. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulate a wave being spawned at a arbitrary location.\nWill not increment the wave counter. lst.explosion = Create an explosion at a location. diff --git a/core/assets/bundles/bundle_nl.properties b/core/assets/bundles/bundle_nl.properties index ad980d279a..a193ffe2dd 100644 --- a/core/assets/bundles/bundle_nl.properties +++ b/core/assets/bundles/bundle_nl.properties @@ -2309,7 +2309,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Spawn unit at a location. lst.applystatus = Apply or clear a status effect from a uniut. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulate a wave being spawned at a arbitrary location.\nWill not increment the wave counter. lst.explosion = Create an explosion at a location. diff --git a/core/assets/bundles/bundle_nl_BE.properties b/core/assets/bundles/bundle_nl_BE.properties index baa57101d7..5691be2512 100644 --- a/core/assets/bundles/bundle_nl_BE.properties +++ b/core/assets/bundles/bundle_nl_BE.properties @@ -2296,7 +2296,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Spawn unit at a location. lst.applystatus = Apply or clear a status effect from a uniut. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulate a wave being spawned at a arbitrary location.\nWill not increment the wave counter. lst.explosion = Create an explosion at a location. diff --git a/core/assets/bundles/bundle_pl.properties b/core/assets/bundles/bundle_pl.properties index afe941105e..e991692472 100644 --- a/core/assets/bundles/bundle_pl.properties +++ b/core/assets/bundles/bundle_pl.properties @@ -2330,7 +2330,7 @@ lst.getblock = Uzyskaj dane dla dowolnej lokalizacji. lst.setblock = Ustaw dane dla dowolnej lokalizacji. lst.spawnunit = Odródź jednostkę w lokalizacji. lst.applystatus = Zastosuj lub wyczyść efekty statusu jednostki. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Symuluj falę odradzającą się w dowolnym miejscu.\nNie zwiększy licznika fali. lst.explosion = Stwórz eksplozję w lokalizacji. diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index 5d854f1e64..aea3126a21 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -2329,7 +2329,7 @@ lst.getblock = Obtenha dados de blocos em qualquer local. lst.setblock = Defina os dados do bloco em qualquer local. lst.spawnunit = Gere uma unidade em um local. lst.applystatus = Aplique ou elimine um efeito de status de uma unidade. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Gerar uma onda. lst.explosion = Crie uma explosão em um local. diff --git a/core/assets/bundles/bundle_pt_PT.properties b/core/assets/bundles/bundle_pt_PT.properties index 0ce81ecf7d..a0be18e995 100644 --- a/core/assets/bundles/bundle_pt_PT.properties +++ b/core/assets/bundles/bundle_pt_PT.properties @@ -2296,7 +2296,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Spawn unit at a location. lst.applystatus = Apply or clear a status effect from a uniut. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulate a wave being spawned at a arbitrary location.\nWill not increment the wave counter. lst.explosion = Create an explosion at a location. diff --git a/core/assets/bundles/bundle_ro.properties b/core/assets/bundles/bundle_ro.properties index da51937357..ed8fc6bb60 100644 --- a/core/assets/bundles/bundle_ro.properties +++ b/core/assets/bundles/bundle_ro.properties @@ -2313,7 +2313,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Spawn unit at a location. lst.applystatus = Apply or clear a status effect from a uniut. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulate a wave being spawned at a arbitrary location.\nWill not increment the wave counter. lst.explosion = Create an explosion at a location. diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index b0b35d7083..2587e514ba 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -2315,7 +2315,7 @@ lst.getblock = Получает данные о плитке в любом ме lst.setblock = Устанавливает плитку в любом месте. lst.spawnunit = Создает боевую единицу на локации. lst.applystatus = Применяет или снимает эффект статуса с боевой единицы. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Имитация волны, создаваемой в произвольном месте.\nСчетчик волн не увеличивается. lst.explosion = Создает взрыв на локации. diff --git a/core/assets/bundles/bundle_sr.properties b/core/assets/bundles/bundle_sr.properties index fac38a433f..56582bbae8 100644 --- a/core/assets/bundles/bundle_sr.properties +++ b/core/assets/bundles/bundle_sr.properties @@ -2316,7 +2316,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Prizovi jedinicu na mestu. lst.applystatus = Dodaj ili ukloni statusni efekat na jedinicu/e. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulate a wave being spawned at a arbitrary location.\nWill not increment the wave counter. lst.explosion = Izazovi eksploziju na mestu. diff --git a/core/assets/bundles/bundle_sv.properties b/core/assets/bundles/bundle_sv.properties index 25e3813f40..65d440cab3 100644 --- a/core/assets/bundles/bundle_sv.properties +++ b/core/assets/bundles/bundle_sv.properties @@ -2296,7 +2296,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Spawn unit at a location. lst.applystatus = Apply or clear a status effect from a uniut. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulate a wave being spawned at a arbitrary location.\nWill not increment the wave counter. lst.explosion = Create an explosion at a location. diff --git a/core/assets/bundles/bundle_th.properties b/core/assets/bundles/bundle_th.properties index 72356a9518..222c4fbb21 100644 --- a/core/assets/bundles/bundle_th.properties +++ b/core/assets/bundles/bundle_th.properties @@ -2333,7 +2333,7 @@ lst.getblock = รับข้อมูลของช่องที่ตำ lst.setblock = ปรับแต่งข้อมูลของช่องที่ตำแหน่งใดๆ lst.spawnunit = เสกยูนิตมาที่ตำแหน่งที่กำหนดไว้ lst.applystatus = ใส่หรือล้างเอฟเฟกต์สถานะจากยูนิต -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = จำลองคลื่นที่ตำแหน่งใดๆ lst.explosion = เสกระเบิดที่ตำแหน่ง diff --git a/core/assets/bundles/bundle_tk.properties b/core/assets/bundles/bundle_tk.properties index 7231208b63..881abd92f9 100644 --- a/core/assets/bundles/bundle_tk.properties +++ b/core/assets/bundles/bundle_tk.properties @@ -2296,7 +2296,7 @@ lst.getblock = Get tile data at any location. lst.setblock = Set tile data at any location. lst.spawnunit = Spawn unit at a location. lst.applystatus = Apply or clear a status effect from a uniut. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Simulate a wave being spawned at a arbitrary location.\nWill not increment the wave counter. lst.explosion = Create an explosion at a location. diff --git a/core/assets/bundles/bundle_tr.properties b/core/assets/bundles/bundle_tr.properties index e7c192a09e..f50ff06f3a 100644 --- a/core/assets/bundles/bundle_tr.properties +++ b/core/assets/bundles/bundle_tr.properties @@ -2313,7 +2313,7 @@ lst.getblock = Herhangi bir yerdeki blok bilgisini al. lst.setblock = Herhangi bir yerdeki blok bilgisini değiştir. lst.spawnunit = Herhangi bir yerde birim var et. lst.applystatus = Bir Birime Durum Etkisi ekle. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Bellir bir noktada dalga başlat.\nDalga Zamanlayıcı Oluşturmaz! lst.explosion = Bir Noktada Patlama oluştur. diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index f640333cb5..e6743cbee4 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -2340,7 +2340,7 @@ lst.getblock = Отримує дані плитки в будь-якому мі lst.setblock = Установлює дані плитки в будь-якому місці. lst.spawnunit = Породжує одиницю на певному місці. lst.applystatus = Застосовує або видаляє ефект стану з одиниці. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Змодельовує хвилю, що виникає у довільному місці.\nНе збільшує лічильник хвиль. lst.explosion = Створює вибух у певному місці. diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index cb1a9d8886..a254aab849 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -2314,7 +2314,7 @@ lst.getblock = Lấy dữ liệu từ ô từ vị trí bất kì. lst.setblock = Chỉnh sửa dữ liệu từ ô từ vị trí bất kì. lst.spawnunit = Tạo ra quân từ vị trí. lst.applystatus = Áp dụng hoặc loại bỏ một hiệu ứng trạng thái cho một đơn vị. -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = Mô phỏng một lượt xuất hiện ở vị trí tùy ý.\nSẽ không tăng số đếm lượt. lst.explosion = Tạo ra một vụ nổ tại vị trí đó. diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index 0337770822..ddd3e316d9 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -2339,7 +2339,7 @@ lst.getblock = 获取任意位置的地块数据 lst.setblock = 设置任意位置的地块数据 lst.spawnunit = 在指定位置生成单位 lst.applystatus = 添加或清除单位的一个状态效果 -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = 在任意位置生成一波敌人\n并不记录在波数计数器中 lst.explosion = 在某个位置生成爆炸 diff --git a/core/assets/bundles/bundle_zh_TW.properties b/core/assets/bundles/bundle_zh_TW.properties index 7fcd78b424..465a18589d 100644 --- a/core/assets/bundles/bundle_zh_TW.properties +++ b/core/assets/bundles/bundle_zh_TW.properties @@ -2325,7 +2325,7 @@ lst.getblock = 由位置取方塊數據 lst.setblock = 由位置設置方塊數據 lst.spawnunit = 在某一位置生成單位 lst.applystatus = 爲單位添加或移除狀態效果 -lst.weathersensor = Check if a type of weather is active. +lst.weathersense = Check if a type of weather is active. lst.weatherset = Set the current state of a type of weather. lst.spawnwave = 在某一位置生成一波敵人\n不計入波數 lst.explosion = 在某一位置製造爆炸 From 47467e5bd20832acf07afa05f70f5e375f24a599 Mon Sep 17 00:00:00 2001 From: Marko Zajc Date: Sun, 31 Mar 2024 04:29:39 +0200 Subject: [PATCH 054/348] Add gradle-wrapper-validation.yml (#9688) --- .github/workflows/gradle-wrapper-validation.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/workflows/gradle-wrapper-validation.yml diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml new file mode 100644 index 0000000000..95cce8bab6 --- /dev/null +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -0,0 +1,10 @@ +name: "Validate Gradle Wrapper" +on: [push, pull_request] + +jobs: + validation: + name: "Validation" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: gradle/wrapper-validation-action@v2 From 396869dec36e58bfb9ec5131a1f1010046eceede Mon Sep 17 00:00:00 2001 From: SITUVNgcd <44901211+SITUVNgcd@users.noreply.github.com> Date: Sun, 31 Mar 2024 10:24:24 +0700 Subject: [PATCH 055/348] Translate & revert Vietnamese translation (#9560) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Translate, revert - Translated new strings. - Fixed some typos. - Corrected translation for some strings. - Revert `Kho lưu trữ` to `Repo`, some Vietnamese need that, idk! https://github.com/Anuken/Mindustry/commit/c3ee92fba701f010b5033aa1d519b6b541d976c2 * Translate new strings https://github.com/Anuken/Mindustry/commit/97010a5064178309b638e4a0adf72add4018bc0a * Remove #note https://github.com/Anuken/Mindustry/commit/97010a5064178309b638e4a0adf72add4018bc0a * World Switch https://github.com/Anuken/Mindustry/commit/76cb36fdea60ea38814765bfaf7f7fde1e812f41 * Translate new strings https://github.com/Anuken/Mindustry/commit/824c6eedb39fba26b41ec8f0843595d2addc7fc0 * Translate new strings https://github.com/Anuken/Mindustry/commit/304d7d9602aefa3df91b4cf1fd81d69fff142644 --------- Co-authored-by: Anuken --- core/assets/bundles/bundle_vi.properties | 299 +++++++++++++---------- 1 file changed, 170 insertions(+), 129 deletions(-) diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index a254aab849..e8be02a566 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -31,8 +31,8 @@ load.map = Bản đồ load.image = Hình ảnh load.content = Nội dung load.system = Hệ thống -load.mod = Mods -load.scripts = Scripts +load.mod = Bản mod +load.scripts = Ngữ lệnh be.update = Đã tìm thấy bản cập nhật mới: be.update.confirm = Tải xuống và khởi động lại ngay bây giờ? @@ -77,9 +77,9 @@ schematic.tags = Thẻ: schematic.edittags = Chỉnh sửa thẻ schematic.addtag = Thêm thẻ schematic.texttag = Thẻ văn bản -schematic.icontag = Thẻ icon +schematic.icontag = Thẻ biểu tượng schematic.renametag = Đổi tên thẻ -schematic.tagged = {0} đã gắn thẻ +schematic.tagged = {0} thẻ đã gắn schematic.tagdelconfirm = Xóa thẻ này? schematic.tagexists = Thẻ đã tồn tại. @@ -243,8 +243,8 @@ host.invalid = [scarlet]Không thể kết nối đến máy chủ. servers.local = Máy chủ cục bộ servers.local.steam = Màn chơi hiện có & Máy chủ cục bộ -servers.remote = Máy chủ tùy chỉnh -servers.global = Máy chủ từ cộng đồng +servers.remote = Máy chủ từ xa +servers.global = Máy chủ cộng đồng servers.disclaimer = Nhà phát triển [accent]không[] sở hữu và kiểm soát máy chủ cộng đồng.\n\nMáy chủ có thể chứa nội dung do người dùng tạo và không phù hợp với mọi lứa tuổi. servers.showhidden = Hiển thị Máy chủ Ẩn @@ -256,7 +256,7 @@ trace = Tìm người chơi trace.playername = Tên người chơi: [accent]{0} trace.ip = IP: [accent]{0} trace.id = ID: [accent]{0} -trace.language = Language: [accent]{0} +trace.language = Ngôn ngữ: [accent]{0} trace.mobile = Máy khách di động: [accent]{0} trace.modclient = Máy khách tùy chỉnh: [accent]{0} trace.times.joined = Số lần tham gia: [accent]{0} @@ -342,23 +342,23 @@ open = Mở customize = Luật tùy chỉnh cancel = Hủy command = Mệnh lệnh -command.queue = [lightgray][Queuing] +command.queue = [lightgray][Đang lệnh tuần tự] command.mine = Đào command.repair = Sửa Chữa command.rebuild = Xây Dựng command.assist = Hỗ Trợ Người Chơi command.move = Di Chuyển command.boost = Tăng Cường -command.enterPayload = Enter Payload Block -command.loadUnits = Load Units -command.loadBlocks = Load Blocks -command.unloadPayload = Unload Payload -stance.stop = Cancel Orders -stance.shoot = Stance: Shoot -stance.holdfire = Stance: Hold Fire -stance.pursuetarget = Stance: Pursue Target -stance.patrol = Stance: Patrol Path -stance.ram = Stance: Ram\n[lightgray]Straight line movement, no pathfinding +command.enterPayload = Nhập khối hàng vào công trình +command.loadUnits = Nhận đơn vị +command.loadBlocks = Nhận khối công trình +command.unloadPayload = Dỡ khối hàng +stance.stop = Hủy mệnh lệnh +stance.shoot = Tư thế: Bắn +stance.holdfire = Tư thế: Ngừng bắn +stance.pursuetarget = Tư thế: Bám đuổi mục tiêu +stance.patrol = Tư thế: Đường tuần tra +stance.ram = Tư thế: Tông thẳng\n[lightgray]Đi theo đường thẳng, không tìm đường openlink = Mở liên kết copylink = Sao chép liên kết back = Quay lại @@ -439,7 +439,7 @@ editor.waves = Lượt: editor.rules = Luật: editor.generation = Cấu trúc: editor.objectives = Mục tiêu -editor.locales = Locale Bundles +editor.locales = Gói ngôn ngữ editor.ingame = Chỉnh sửa trong trò chơi editor.playtest = Chơi thử editor.publish.workshop = Xuất bản lên Workshop @@ -459,7 +459,7 @@ waves.title = Đợt waves.remove = Xóa waves.every = mỗi waves.waves = đợt -waves.health = health: {0}% +waves.health = độ bền: {0}% waves.perspawn = mỗi lần xuất hiện waves.shields = khiên/đợt waves.to = đến @@ -480,7 +480,7 @@ waves.none = Không có kẻ thù được xác định.\nLưu ý rằng bố c waves.sort = Sắp xếp theo waves.sort.reverse = Đảo ngược sắp xếp waves.sort.begin = Bắt đầu -waves.sort.health = Máu +waves.sort.health = Độ bền waves.sort.type = Thể loại waves.search = Tìm kiếm các lượt... waves.filter = Bộ lọc đơn vị @@ -490,13 +490,13 @@ waves.units.show = Hiện tất cả #these are intentionally in lower case wavemode.counts = số lượng wavemode.totals = tổng số -wavemode.health = máu +wavemode.health = độ bền editor.default = [lightgray] details = Chi tiết... edit = Chỉnh sửa... variables = Thông số -logic.globals = Built-in Variables +logic.globals = Thông số sẵn có editor.name = Tên: editor.spawn = Thêm kẻ địch editor.removeunit = Xóa kẻ địch @@ -508,7 +508,7 @@ editor.errorlegacy = Bản đồ này quá cũ, và sử dụng định dạng b editor.errornot = Đây không phải là tệp bản đồ. editor.errorheader = Tệp bản đồ này không hợp lệ hoặc bị hỏng. editor.errorname = Bản đồ không có tên được xác định. Bạn đang cố gắng tải một bản lưu? -editor.errorlocales = Error reading invalid locale bundles. +editor.errorlocales = Lỗi đọc gói ngôn ngữ không hợp lệ. editor.update = Cập nhật editor.randomize = Ngẫu nhiên editor.moveup = Di chuyển lên @@ -520,7 +520,7 @@ editor.sectorgenerate = Tạo ra khu vực editor.resize = Thay đổi kích thước editor.loadmap = Mở bản đồ editor.savemap = Lưu bản đồ -editor.savechanges = [scarlet]You have unsaved changes!\n\n[]Do you want to save them? +editor.savechanges = [scarlet]Bạn có thay đổi chưa lưu!\n\n[]Bạn có muốn lưu chúng không? editor.saved = Đã lưu! editor.save.noname = Bản đồ của bạn không có tên! Hãy đặt một cái tên trong 'Thông tin bản đồ'. editor.save.overwrite = Bản đồ của bạn ghi đè lên một bản đồ đã có sẵn! Hãy chọn một cái tên khác trong 'Thông tin bản đồ'. @@ -607,23 +607,23 @@ filter.option.floor2 = Nền phụ filter.option.threshold2 = Ngưỡng phụ filter.option.radius = Bán kính filter.option.percentile = Phần trăm -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) -locales.deletelocale = Are you sure you want to delete this locale bundle? -locales.applytoall = Apply Changes To All Locales -locales.addtoother = Add To Other Locales -locales.rollback = Rollback to last applied -locales.filter = Property filter -locales.searchname = Search name... -locales.searchvalue = Search value... -locales.searchlocale = Search locale... -locales.byname = By name -locales.byvalue = By value -locales.showcorrect = Show properties that are present in all locales and have unique values everywhere -locales.showmissing = Show properties that are missing in some locales -locales.showsame = Show properties that have same values in different locales -locales.viewproperty = View in all locales -locales.viewing = Viewing property "{0}" -locales.addicon = Add Icon +locales.info = Tại đây, bạn có thể thêm gói ngôn ngữ cho ngôn ngữ cụ thể vào bản đồ của bạn. Trong gói ngôn ngữ, mỗi thuộc tính có tên và giá trị. Những thuộc tính này có thể được sử dụng bởi Bộ xử lý thế giới và Mục tiêu nhiệm vụ bằng tên của chúng. Chúng hỗ trợ định dạng văn bản (thay thế kí tự giữ chỗ bằng giá trị thực tế).\n\n[cyan]Ví dụ thuộc tính:\n[]tên: [accent]timer[]\nvalue: [accent]Bộ đếm thời gian ví dụ, thời gian còn lại: @[]\n\n[cyan]Cách dùng:\n[]Đặt làm văn bản cho Mục tiêu nhiệm vụ: [accent]@timer\n\n[]In nó trong Bộ xử lý thế giới:\n[accent]localeprint "timer"\nformat time\n[gray](với time là biến được tính toán riêng biệt) +locales.deletelocale = Bạn có chắc muốn xóa gói ngôn ngữ này? +locales.applytoall = Áp dụng thay đổi cho tất cả gói ngôn ngữ +locales.addtoother = Thêm vào các gói ngôn ngữ khác +locales.rollback = Khôi phục lại lầ áp dụng cuối +locales.filter = Lọc thuộc tính +locales.searchname = Tìm tên... +locales.searchvalue = Tìm giá trị... +locales.searchlocale = Tìm ngôn ngữ... +locales.byname = Theo tên +locales.byvalue = Theo giá trị +locales.showcorrect = Hiện thuộc tính có trong tất cả ngôn ngữ và có giá trị riêng biệt mỗi nơi +locales.showmissing = Hiện thuộc tính bị mất trong một số ngôn ngữ +locales.showsame = Hiện thuộc tính có giá trị giống nhau trong các ngôn ngữ khác nhau +locales.viewproperty = Xem trong tất cả ngôn ngữ +locales.viewing = Đang xem thuộc tính "{0}" +locales.addicon = Thêm biểu tượng width = Chiều rộng: height = Chiều cao: @@ -674,11 +674,11 @@ objective.destroycore.name = Phá huỷ căn cứ objective.commandmode.name = Chế độ ra lệnh objective.flag.name = Cờ marker.shapetext.name = Hình dạng văn bản -marker.point.name = Point +marker.point.name = Điểm marker.shape.name = Hình dạng marker.text.name = Văn bản -marker.line.name = Line -marker.quad.name = Quad +marker.line.name = Đường kẻ +marker.quad.name = Bốn điểm marker.background = Nền marker.outline = Đường viền objective.research = [accent]Nghiên cứu:\n[]{0}[lightgray]{1} @@ -762,8 +762,8 @@ sector.curlost = Khu vực đã mất sector.missingresources = [scarlet]Không đủ tài nguyên căn cứ sector.attacked = Khu vực [accent]{0}[white] đang bị tấn công! sector.lost = Khu vực [accent]{0}[white] đã mất! -sector.capture = Sector [accent]{0}[white]Captured! -sector.capture.current = Sector Captured! +sector.capture = Khu vực [accent]{0}[white] đã chiếm! +sector.capture.current = Khu vực đã chiếm! sector.changeicon = Thay đổi biểu tượng sector.noswitch.title = Không thể thay đổi sang khu vực khác sector.noswitch = Bạn không thể đổi sang khu vực khác khi một khu vực đang bị tấn công.\n\nKhu vực: [accent]{0}[] ở [accent]{1}[] @@ -986,6 +986,46 @@ stat.immunities = Miễn nhiễm stat.healing = Sửa chữa ability.forcefield = Tạo khiên +ability.forcefield.description = Phát một khiên trường lực hấp thụ các loại đạn +ability.repairfield = Sửa chữa/Xây dựng +ability.repairfield.description = Sửa chữa các đơn vị gần đó +ability.statusfield = Vùng gia tốc +ability.statusfield.description = Áp dụng hiệu ứng trạng thái vào các đơn vị gần đó +ability.unitspawn = Sản xuất +ability.unitspawn.description = Sản xuất đơn vị +ability.shieldregenfield = Tạo khiên nhỏ +ability.shieldregenfield.description = Tạo khiên cho các đơn vị gần đó +ability.movelightning = Phóng điện khi di chuyển +ability.movelightning.description = Giải phóng tia điện khi di chuyển +ability.armorplate = Mảnh giáp +ability.armorplate.description = Giảm sát thương gánh chịu trong khi bắn +ability.shieldarc = Khiên vòng cung +ability.shieldarc.description = Phát một khiên trường lực vòng cung hấp thụ các loại đạn +ability.suppressionfield = Ngăn chặn sửa chữa +ability.suppressionfield.description = Ngăn chặn các công trình sửa chữa +ability.energyfield = Trường điện từ +ability.energyfield.description = Giật điện các kẻ thù gần đó +ability.energyfield.healdescription = Giật điện các kẻ thù gần đó và hồi phục đồng minh +ability.regen = Hồi phục +ability.regen.description = Tự hồi phục theo thời gian +ability.liquidregen = Hấp thụ chất lỏng +ability.liquidregen.description = Hấp thụ chất lỏng để tự hồi phục +ability.spawndeath = Chết sản sinh +ability.spawndeath.description = Sinh ra đơn vị khi chết +ability.liquidexplode = Chết tràn dịch +ability.liquidexplode.description = Tràn chất lỏng khi chết +ability.stat.firingrate = tốc bắn [stat]{0}/giây[lightgray] +ability.stat.regen = [stat]{0}[lightgray] độ bền/giây +ability.stat.shield = [stat]{0}[lightgray] khiên +ability.stat.repairspeed = [stat]{0}/giây[lightgray] tốc độ sửa chữa +ability.stat.slurpheal = [stat]{0}[lightgray] độ bền/đơn vị chất lỏng +ability.stat.cooldown = [stat]{0} giây[lightgray] hồi chiêu +ability.stat.maxtargets = [stat]{0}[lightgray] mục tiêu tối đa +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] số lượng sửa chữa cùng kiểu +ability.stat.damagereduction = [stat]{0}%[lightgray] giảm sát thương +ability.stat.minspeed = tốc độ tối thiểu [stat]{0} ô/giây[lightgray] +ability.stat.duration = thời hạn [stat]{0} giây[lightgray] +ability.stat.buildtime = thời gian xây [stat]{0} giây[lightgray] ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Sửa chữa/Xây dựng ability.repairfield.description = Repairs nearby units @@ -1065,7 +1105,7 @@ bullet.splashdamage = [stat]{0}[lightgray] sát thương diện rộng ~[stat] { bullet.incendiary = [stat]cháy bullet.homing = [stat]truy đuổi bullet.armorpierce = [stat]xuyên giáp -bullet.maxdamagefraction = [stat]{0}%[lightgray] damage limit +bullet.maxdamagefraction = [stat]{0}%[lightgray] giới hạn sát thương bullet.suppression = [stat]{0} giây[lightgray] ngăn sửa chữa ~ [stat]{1}[lightgray] ô bullet.interval = [stat]{0}/giây[lightgray] interval bullets: bullet.frags = [stat]phá mảnh @@ -1120,8 +1160,8 @@ setting.logichints.name = Gợi ý Logic setting.backgroundpause.name = Tạm dừng trong nền setting.buildautopause.name = Tự động dừng xây dựng setting.doubletapmine.name = Nhấn đúp để Đào -setting.commandmodehold.name = Nhấn giữ để vào chế độ khiển quân -setting.distinctcontrolgroups.name = Limit One Control Group Per Unit +setting.commandmodehold.name = Nhấn giữ để vào chế độ khiển đơn vị +setting.distinctcontrolgroups.name = Giới hạn một nhóm điều khiển cho mỗi đơn vị setting.modcrashdisable.name = Tắt các mod khi gặp sự cố trong khởi động setting.animatedwater.name = Hiệu ứng nước setting.animatedshields.name = Hiệu ứng khiên @@ -1168,14 +1208,14 @@ setting.position.name = Hiển thị vị trí người chơi setting.mouseposition.name = Hiện vị trí trỏ chuột setting.musicvol.name = Âm lượng nhạc setting.atmosphere.name = Hiển thị bầu khí quyển hành tinh -setting.drawlight.name = Draw Darkness/Lighting +setting.drawlight.name = Vẽ Bóng tối/Ánh sáng setting.ambientvol.name = Âm lượng tổng setting.mutemusic.name = Tắt nhạc setting.sfxvol.name = Âm lượng SFX setting.mutesound.name = Tắt tiếng setting.crashreport.name = Gửi báo cáo sự cố setting.savecreate.name = Tự động lưu -setting.steampublichost.name = Public Game Visibility +setting.steampublichost.name = Hiển thị trò chơi công khai setting.playerlimit.name = Giới hạn người chơi setting.chatopacity.name = Độ mờ trò chuyện setting.lasersopacity.name = Độ mờ kết nối năng lượng @@ -1195,7 +1235,7 @@ keybind.title = Sửa phím keybinds.mobile = [scarlet]Hầu hết phím ở đây không hoạt động trên thiết bị di động. Chỉ hỗ trợ di chuyển cơ bản. category.general.name = Chung category.view.name = Xem -category.command.name = Unit Command +category.command.name = Mệnh lệnh đơn vị category.multiplayer.name = Nhiều người chơi category.blocks.name = Chọn khối placement.blockselectkeys = \n[lightgray]Phím: [{0}, @@ -1212,24 +1252,24 @@ keybind.move_y.name = Di chuyển Y keybind.mouse_move.name = Theo chuột keybind.pan.name = Di chuyển góc nhìn keybind.boost.name = Tăng tốc -keybind.command_mode.name = Chế độ điều khiển quân -keybind.command_queue.name = Unit Command Queue -keybind.create_control_group.name = Create Control Group -keybind.cancel_orders.name = Cancel Orders -keybind.unit_stance_shoot.name = Unit Stance: Shoot -keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire -keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target -keybind.unit_stance_patrol.name = Unit Stance: Patrol -keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.command_mode.name = Chế độ mệnh lệnh +keybind.command_queue.name = Lệnh tuần tự đơn vị +keybind.create_control_group.name = Tạo nhóm điều khiển +keybind.cancel_orders.name = Hủy lệnh +keybind.unit_stance_shoot.name = Tư thế đơn vị: Bắn +keybind.unit_stance_hold_fire.name = Tư thế đơn vị: Ngừng bắn +keybind.unit_stance_pursue_target.name = Tư thế đơn vị: Bám đuổi mục tiêu +keybind.unit_stance_patrol.name = Tư thế đơn vị: Tuần tra +keybind.unit_stance_ram.name = Tư thế đơn vị: Tông thẳng +keybind.unit_command_move = Mệnh lệnh đơn vị: Di chuyển +keybind.unit_command_repair = Mệnh lệnh đơn vị: Sửa chữa +keybind.unit_command_rebuild = Mệnh lệnh đơn vị: Xây lại +keybind.unit_command_assist = Mệnh lệnh đơn vị: Hỗ trợ +keybind.unit_command_mine = Mệnh lệnh đơn vị: Khai thác +keybind.unit_command_boost = Mệnh lệnh đơn vị: Tăng cường l +keybind.unit_command_load_units = Mệnh lệnh đơn vị: Nhập đơn vị +keybind.unit_command_load_blocks = Mệnh lệnh đơn vị: Nhập khối công trình +keybind.unit_command_unload_payload = Mệnh lệnh đơn vị: Dỡ khối hàng keybind.rebuild_select.name = Chọn khu vực xây dựng lại keybind.schematic_select.name = Chọn khu vực keybind.schematic_menu.name = Menu bản thiết kế @@ -1293,12 +1333,12 @@ mode.pvp.description = Chiến đấu với những người chơi khác trên c mode.attack.name = Tấn công mode.attack.description = Phá hủy căn cứ của kẻ địch. \n[gray]Cần căn cứ màu đỏ trong bản đồ để chơi. mode.custom = Tùy chỉnh luật -rules.invaliddata = Invalid clipboard data. -rules.hidebannedblocks = Ẩn Các Khối Bị Cấm +rules.invaliddata = Dữ liệu bộ nhớ tạm không hợp lệ. +rules.hidebannedblocks = Ẩn các khối bị cấm rules.infiniteresources = Tài nguyên vô hạn rules.onlydepositcore = Chỉ cho phép đưa tài nguyên vào căn cứ -rules.derelictrepair = Allow Derelict Block Repair +rules.derelictrepair = Cho phép sửa khối bỏ hoang rules.reactorexplosions = Nổ lò phản ứng rules.coreincinerates = Hủy vật phẩm khi căn cứ đầy rules.disableworldprocessors = Vô hiệu hoá bộ xử lý thế giới @@ -1322,11 +1362,12 @@ rules.blockhealthmultiplier = Hệ số độ bền khối rules.blockdamagemultiplier = Hệ số sát thương của khối rules.unitbuildspeedmultiplier = Hệ số tốc độ sản xuất lính rules.unitcostmultiplier = Hệ số yêu cầu tài nguyên sản xuất đơn vị -rules.unithealthmultiplier = Hệ số máu của đơn vị +rules.unithealthmultiplier = Hệ số độ bền của đơn vị rules.unitdamagemultiplier = Hệ số sát thương của đơn vị rules.unitcrashdamagemultiplier = Hệ số sát thương của đơn vị khi bị bắn rơi rules.solarmultiplier = Hệ số năng lượng mặt trời rules.unitcapvariable = Căn cứ tăng giới hạn đơn vị +rules.unitpayloadsexplode = Khối hàng mang theo phát nổ cùng đơn vị rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Giới hạn đơn vị rules.limitarea = Giới hạn kích thước bản đồ @@ -1579,7 +1620,7 @@ block.inverted-sorter.name = Bộ lọc ngược block.message.name = Thông điệp block.reinforced-message.name = Thông điệp [Gia cố] block.world-message.name = Thông điệp thế giới -block.world-switch.name = World Switch +block.world-switch.name = Công tắc thế giới block.illuminator.name = Đèn block.overflow-gate.name = Cổng tràn block.underflow-gate.name = Cổng tràn ngược @@ -1856,7 +1897,7 @@ block.memory-bank.name = Bộ nhớ lớn team.malis.name = Malis team.crux.name = Crux team.sharded.name = Sharded -team.derelict.name = Không xác định +team.derelict.name = Bỏ hoang team.green.name = Xanh lá cây team.blue.name = Xanh dương @@ -1871,7 +1912,7 @@ hint.desktopPause = Nhấn [accent][[Space][] để tạm dừng và tiếp tụ hint.breaking = [accent]Chuột phải[] và kéo để phá vỡ các khối. hint.breaking.mobile = Kích hoạt \ue817 [accent]Cây búa[] ở phía dưới cùng bên phải và nhấn để phá vỡ các khối.\n\nGiữ ngón tay của bạn trong một giây và kéo để phá khối trong vùng được chọn. hint.blockInfo = Xem thông tin của một khối bằng cách chọn nó trong [accent]menu xây dựng[], Sau đó chọn nút [accent][[?][] ở bên phải. -hint.derelict = [accent]Không xác định[] là các công trình bị hỏng của các căn cứ cũ mà không còn hoạt động.\n\nCác công trình này có thể [accent]được tháo dỡ[] để nhận được nguyên liệu. +hint.derelict = [accent]Bỏ hoang[] là các công trình bị hỏng của các căn cứ cũ mà không còn hoạt động.\n\nCác công trình này có thể [accent]được tháo dỡ[] để nhận được nguyên liệu. hint.research = Sử dụng nút \ue875 [accent]Nghiên cứu[] để nghiên cứu công nghệ mới. hint.research.mobile = Sử dụng nút \ue875 [accent]Nghiên cứu[] trong \ue88c [accent]Trình đơn[] để nghiên cứu công nghệ mới. hint.unitControl = Giữ [accent][[L-ctrl][] và [accent]nhấp chuột[] để điều khiển đơn vị của bạn hoặc súng. @@ -1937,13 +1978,13 @@ onset.turrets = Các đơn vị rất tốt, nhưng [accent]súng[] cung cấp k onset.turretammo = Tiếp đạn cho súng bằng [accent]beryllium[]. onset.walls = [accent]Tường[] có thể ngăn chặn sát thương đến các công trình.\nĐặt một số \uf6ee [accent]tường beryllium[] xung quanh súng. onset.enemies = Quân địch đang đến, hãy chuẩn bị phòng thủ. -onset.defenses = [accent]Set up defenses:[lightgray] {0} +onset.defenses = [accent]Thiết lập phòng thủ:[lightgray] {0} onset.attack = Quân địch đã suy yếu.\nHãy phản công. onset.cores = Các căn cứ có thể được đặt trên [accent]ô căn cứ[].\nCác căn cứ mới có thể được đặt ở bất kỳ đâu trên bản đồ.\nĐặt một \uf725 căn cứ. onset.detect = Quân địch sẽ phát hiện bạn trong vòng 2 phút.\nHãy chuẩn bị phòng thủ, khai thác và sản xuất. onset.commandmode = Giữ [accent]Shift[] để vào [accent]chế độ điều khiển quân[].\n[accent]Nhấp chuột trái và kéo[] để chọn các đơn vị.\n[accent]Chuộc phải[] để điều khiển các đơn vị di chuyển hoặc tấn công. onset.commandmode.mobile = Nhấn vào [accent]nút điều khiển[] để vào [accent]chế độ điều khiển quân[].\nGiữ một ngón tay, sau đó [accent]kéo[] để chọn các đơn vị.\n[accent]Nhấp[] để điều khiển các đơn vị di chuyển hoặc tấn công. -aegis.tungsten = Tungsten can be mined using an [accent]impact drill[].\nThis structure requires [accent]water[] and [accent]power[]. +aegis.tungsten = Tungsten có thể khai thác bằng [accent]khoan thủy lực[].\nCông trình này cần có [accent]nước[] và [accent]năng lượng[]. split.pickup = Một số khối có thể được mang bởi đơn vị.\nNhấp vào [accent]container[] và đặt nó lên [accent]máy nạp vật phẩm[].\n(Phím mặc định là [ và ] để mang và thả) split.pickup.mobile = Một số khối có thể được mang bởi đơn vị.\nNhấp vào [accent]container[] và đặt nó lên [accent]máy nạp vật phẩm[].\n(Để mang hoặc thả một khối, ấn giữ nó một chút.) split.acquire = Bạn cần một số tungsten để sản xuất đơn vị. @@ -1988,7 +2029,7 @@ liquid.nitrogen.description = Được sử dụng trong khai thác tài nguyên liquid.neoplasm.description = Một sản phẩm phụ sinh học nguy hiểm của lò phản ứng Neoplasia. Lan nhanh sang bất kì khối chứa nước nào mà nó chạm vào và gây hư hại chúng. Nhớt. liquid.neoplasm.details = Neoplasm. Một khối lượng các tế bào tổng hợp phân chia nhanh chóng không kiểm soát với độ đặc giống như bùn. Kháng nhiệt. Cực kì nguy hiểm cho bất cứ khối nào có liên quan đến nước.\n\nQuá phức tạp và không ổn định để được phân tích. Chưa rõ được tiềm năng và ứng dụng của nó. Khuyến nghị đốt chúng trong xỉ nóng chảy -block.derelict = \uf77e [lightgray]Không xác định +block.derelict = \uf77e [lightgray]Bỏ hoang block.armored-conveyor.description = Vận chuyển vật phẩm về phía trước. Không nhận đầu vào từ phía bên cạnh. block.illuminator.description = Phát sáng. block.message.description = Lưu trữ tin nhắn giao tiếp giữa đồng đội. @@ -2128,7 +2169,7 @@ block.additive-reconstructor.description = Nâng cấp quân của bạn lên c block.multiplicative-reconstructor.description = Nâng cấp quân của bạn lên cấp ba. block.exponential-reconstructor.description = Nâng cấp quân của bạn lên cấp bốn. block.tetrative-reconstructor.description = Nâng cấp quân của bạn lên cấp năm (cuối cùng). -block.switch.description = Công tắc, trạng thái có thể được đọc và điều khiển với vi xử lý logic. +block.switch.description = Công tắc, trạng thái có thể được đọc và điều khiển với xử lý logic. block.micro-processor.description = Chạy tập hợp các chỉ dẫn trong một vòng lặp, có thể dùng để điều khiển robot và công trình. block.logic-processor.description = Chạy tập hợp các chỉ dẫn trong một vòng lặp, có thể dùng để điều khiển robot và công trình. Nhanh hơn bộ xử lý nhỏ. block.hyper-processor.description = Chạy tập hợp các chỉ dẫn trong một vòng lặp, có thể dùng để điều khiển robot và công trình. Nhanh hơn bộ xử lý. @@ -2291,10 +2332,10 @@ unit.emanate.description = Xây công trình để phòng thủ lõi Acropolis. lst.read = Đọc một số từ bộ nhớ được liên kết. lst.write = Ghi một số vào bộ nhớ được liên kết. lst.print = Thêm văn bản vào bộ nhớ in.\nKhông hiển thị gì cho đến khi sử dụng [accent]Print Flush[]. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" lst.draw = Thêm một thao tác vào bộ nhớ vẽ.\nKhông hiển thị gì cho đến khi sử dụng [accent]Draw Flush[]. lst.drawflush = Chuyển các thao tác [accent]Draw[] đến màn hình. lst.printflush = Chuyển các thao tác [accent]Print[] đến khối tin nhắn. +lst.format = Thay thế kí tự giữ chỗ tiếp theo ("[accent]@[]") trong bộ đệm văn bản bằng giá trị.\nVí dụ:\n[accent]print "test @"\nformat "example" lst.getlink = Nhận liên kết bộ xử lý theo thứ tự. Bắt đầu từ 0. lst.control = Điều khiển một khối. lst.radar = Định vị các đơn vị trong phạm vi xung quanh một khối. @@ -2314,8 +2355,8 @@ lst.getblock = Lấy dữ liệu từ ô từ vị trí bất kì. lst.setblock = Chỉnh sửa dữ liệu từ ô từ vị trí bất kì. lst.spawnunit = Tạo ra quân từ vị trí. lst.applystatus = Áp dụng hoặc loại bỏ một hiệu ứng trạng thái cho một đơn vị. -lst.weathersense = Check if a type of weather is active. -lst.weatherset = Set the current state of a type of weather. +lst.weathersense = Kiểm tra kiểu thời tiết đang hoạt động. +lst.weatherset = Thiết lập trạng thái hiện tại của kiểu thời tiết. lst.spawnwave = Mô phỏng một lượt xuất hiện ở vị trí tùy ý.\nSẽ không tăng số đếm lượt. lst.explosion = Tạo ra một vụ nổ tại vị trí đó. lst.setrate = Đặt tốc độ thực thi khối xử lý theo chỉ thị/tíc-tắc. @@ -2329,45 +2370,45 @@ lst.getflag = Kiểm tra nếu cờ toàn cục được đặt. lst.setprop = Đặt một thuộc tính của đơn vị hoặc công trình. lst.effect = Tạo một phần hiệu ứng nhỏ. lst.sync = Đồng bộ giá trị biến qua mạng.\nChỉ gọi tối đa 10 lần mỗi giây. -lst.makemarker = Create a new logic marker in the world.\nAn ID to identify this marker must be provided.\nMarkers currently limited to 20,000 per world. -lst.setmarker = Set a property for a marker.\nThe ID used must be the same as in the Make Marker instruction. -lst.localeprint = Add map locale property value to the text buffer.\nTo set map locale bundles in map editor, check [accent]Map Info > Locale Bundles[].\nIf client is a mobile device, tries to print a property ending in ".mobile" first. +lst.makemarker = Tạo mới điểm đánh dấu lô-gic trên thế giới.\n Một định danh cho điểm đánh dấu này phải được cung cấp.\nĐiểm đánh dấu hiện tại bị giới hạn 20,000 trên mỗi thế giới. +lst.setmarker = Lập một thuộc tính cho một điểm đánh dấu.\n Định danh phải giống như định danh ở chỉ dẫn lệnh Tạo Điểm đánh dấu (Make Marker).\nGiá trị [accent]null [] sẽ bị phớt lờ thay vì chuyển thành 0. +lst.localeprint = Thêm một giá trị thuộc tính ngôn ngữ của bản đồ vào bộ đệm văn bản.\nĐể thiết đặt gói ngôn ngữ trong trình chỉnh sửa bản đồ, xem qua [accent]Thông tin bản đồ > Gói ngôn ngữ[].\nNếu máy khách là một thiết bị di động, sẽ cố in thuộc tính kết thúc bằng ".mobile" trước tiên. lglobal.false = 0 lglobal.true = 1 lglobal.null = null -lglobal.@pi = The mathematical constant pi (3.141...) -lglobal.@e = The mathematical constant e (2.718...) -lglobal.@degToRad = Multiply by this number to convert degrees to radians -lglobal.@radToDeg = Multiply by this number to convert radians to degrees -lglobal.@time = Playtime of current save, in milliseconds -lglobal.@tick = Playtime of current save, in ticks (1 second = 60 ticks) -lglobal.@second = Playtime of current save, in seconds -lglobal.@minute = Playtime of current save, in minutes -lglobal.@waveNumber = Current wave number, if waves are enabled -lglobal.@waveTime = Countdown timer for waves, in seconds -lglobal.@mapw = Map width in tiles -lglobal.@maph = Map height in tiles -lglobal.sectionMap = Map -lglobal.sectionGeneral = General -lglobal.sectionNetwork = Network/Clientside [World Processor Only] -lglobal.sectionProcessor = Processor -lglobal.sectionLookup = Lookup -lglobal.@this = The logic block executing the code -lglobal.@thisx = X coordinate of block executing the code -lglobal.@thisy = Y coordinate of block executing the code -lglobal.@links = Total number of blocks linked to this processors -lglobal.@ipt = Execution speed of the processor in instructions per tick (60 ticks = 1 second) -lglobal.@unitCount = Total number of types of unit content in the game; used with the lookup instruction -lglobal.@blockCount = Total number of types of block content in the game; used with the lookup instruction -lglobal.@itemCount = Total number of types of item content in the game; used with the lookup instruction -lglobal.@liquidCount = Total number of types of liquid content in the game; used with the lookup instruction -lglobal.@server = True if the code is running on a server or in singleplayer, false otherwise -lglobal.@client = True if the code is running on a client connected to a server -lglobal.@clientLocale = Locale of the client running the code. For example: en_US -lglobal.@clientUnit = Unit of client running the code -lglobal.@clientName = Player name of client running the code -lglobal.@clientTeam = Team ID of client running the code -lglobal.@clientMobile = True is the client running the code is on mobile, false otherwise +lglobal.@pi = Hằng số toán học pi (3.141...) +lglobal.@e = Hằng số toán học e (2.718...) +lglobal.@degToRad = Nhân với số này để chuyển từ độ (deg) sang radian (rad) +lglobal.@radToDeg = Nhân với số này để chuyển từ radian (rad) sang độ (deg) +lglobal.@time = Thời gian chơi của bản lưu hiện tại, tính bằng mili-giây +lglobal.@tick = Thời gian chơi của bản lưu hiện tại, tính bằng tích-tắc (1 giây = 60 tích-tắc) +lglobal.@second = Thời gian chơi của bản lưu hiện tại, tính bằng giây +lglobal.@minute = Thời gian chơi của bản lưu hiện tại, tính bằng phút +lglobal.@waveNumber = Số lượt hiện tại, nếu chế độ lượt được bật +lglobal.@waveTime = Thời gian đếm ngược của lượt, tính bằng giây +lglobal.@mapw = Độ rộng bản đồ, tính bằng ô +lglobal.@maph = Độ cao bản đồ, tính bằng ô +lglobal.sectionMap = Bản đồ +lglobal.sectionGeneral = Chung +lglobal.sectionNetwork = Mạng/Máy khách [Chỉ Bộ xử lý thế giới] +lglobal.sectionProcessor = Bộ xử lý +lglobal.sectionLookup = Tra cứu +lglobal.@this = Khối lô-gíc đang thực thi đoạn mã +lglobal.@thisx = Tọa độ x của khối đang thực thi đoạn mã +lglobal.@thisy = Tọa độ y của khối đang thực thi đoạn mã +lglobal.@links = Tổng số khối đã liên kết đến bộ xử lý này +lglobal.@ipt = Tốc độ thực thi của bộ xử lý tính bằng lệnh mỗi tích-tắc (60 tích-tắc = 1 giây) +lglobal.@unitCount = Tổng số kiểu mẫu đơn vị trong trò chơi; được dùng với lệnh tra cứu +lglobal.@blockCount = Tổng số kiểu mẫu khối trong trò chơi; được dùng với lệnh tra cứu +lglobal.@itemCount = Tổng số kiểu mẫu vật phẩm trong trò chơi; được dùng với lệnh tra cứu +lglobal.@liquidCount = Tổng số kiểu mẫu chất lỏng trong trò chơi; được dùng với lệnh tra cứu +lglobal.@server = True nếu đoạn mã chạy trên máy chủ hoặc chơi đơn, ngược lại là false +lglobal.@client = True nếu đoạn mã chạy trên máy khách được kết nối đến máy chủ +lglobal.@clientLocale = Ngôn ngữ của máy khách đang chạy đoạn mã. Ví dụ: vi_VN +lglobal.@clientUnit = Đơn vị máy khách đang chạy đoạn mã +lglobal.@clientName = Tên người chơi của máy khách đang chạy đoạn mã +lglobal.@clientTeam = Định danh đội của máy khách đang chạy đoạn mã +lglobal.@clientMobile = True nếu máy khách đang chạy đoạn mã trên thiết bị di động, ngược lại là false logic.nounitbuild = [red]Lô-gíc xây dựng đơn vị không được phép ở đây. @@ -2410,7 +2451,7 @@ graphicstype.poly = Tô vào đa giác đều. graphicstype.linepoly = Vẽ đường viền đa giác đều. graphicstype.triangle = Tô một hình tam giác. graphicstype.image = Vẽ hình ảnh một số nội dung.\nVí dụ: [accent]@router[] hoặc [accent]@dagger[]. -graphicstype.print = Draws text from the print buffer.\nClears the print buffer. +graphicstype.print = Vẽ văn bản từ bộ đệm in ra.\nLàm sạch bộ đệm. lenum.always = Luôn đúng. lenum.idiv = Chia lấy phần nguyên. @@ -2519,10 +2560,10 @@ lenum.build = Xây công trình. lenum.getblock = Lấy một cấu trúc và kiểu tại một tọa độ.\nĐơn vị phải nằm trong tầm của vị trí.\nKhối rắn không phải công trình có kiểu [accent]@solid[]. lenum.within = Kiểm tra xem đơn vị có gần vị trí không. lenum.boost = Bắt đầu/Dừng tăng tốc. -lenum.flushtext = Flush print buffer's content to marker, if applicable.\nIf fetch is set to true, tries to fetch properties from map locale bundle or game's bundle. -lenum.texture = Texture name straight from game's texture atlas (using kebab-case naming style).\nIf printFlush is set to true, consumes text buffer content as text argument. -lenum.texturesize = Size of texture in tiles. Zero value scales marker width to original texture's size. -lenum.autoscale = Whether to scale marker corresponding to player's zoom level. -lenum.posi = Indexed position, used for line and quad markers with index zero being the first position. -lenum.uvi = Texture's position ranging from zero to one, used for quad markers. -lenum.colori = Indexed position, used for line and quad markers with index zero being the first color. +lenum.flushtext = Lấp đầy nội dung của bộ đệm cho điểm đánh dấu, nếu có thể áp dụng.\n Nếu [accent]fetch[] gắn là [accent]true[], sẽ cố truy vấn các thuộc tính từ gói ngôn ngữ của bản đồ hoặc trò chơi. +lenum.texture = Tên kết cấu lấy thẳng từ bản khung kết cấu của trò chơi (dùng kiểu tên kebab-case, tên-chữ-thường-chứa-gạch-nối).\nNếu printFlush gán bằng true, sẽ sử dụng nội dung bộ đệm văn bản làm tham số văn bản. +lenum.texturesize = Kích thước của kết cấu bằng ô. Giá trị 0 chia tỷ lệ chiều rộng của điểm đánh dấu theo kích thước của kết cấu ban đầu. +lenum.autoscale = Có chia tỷ lệ điểm đánh dấu tương ứng với mức thu phóng của người chơi hay không. +lenum.posi = Vị trí theo chỉ số, dùng cho điểm đánh dấu đường kẻ (line) và bốn điểm (quad) với 0 là vị trí đầu tiên. +lenum.uvi = Vị trí của kết cấu trong phạm vi từ 0 đến 1, dùng cho đánh dấu bốn điểm (quad). +lenum.colori = Màu theo chỉ số, dùng cho điểm đánh dấu đường kẻ (line) và bốn điểm (quad) với 0 là màu đầu tiên. \ No newline at end of file From a14e8d61898456b20bfc0a87c6d6b1032e619c9a Mon Sep 17 00:00:00 2001 From: Github Actions Date: Sun, 31 Mar 2024 03:25:10 +0000 Subject: [PATCH 056/348] Automatic bundle update --- core/assets/bundles/bundle_vi.properties | 45 ++---------------------- 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index e8be02a566..393ff981cb 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -986,46 +986,6 @@ stat.immunities = Miễn nhiễm stat.healing = Sửa chữa ability.forcefield = Tạo khiên -ability.forcefield.description = Phát một khiên trường lực hấp thụ các loại đạn -ability.repairfield = Sửa chữa/Xây dựng -ability.repairfield.description = Sửa chữa các đơn vị gần đó -ability.statusfield = Vùng gia tốc -ability.statusfield.description = Áp dụng hiệu ứng trạng thái vào các đơn vị gần đó -ability.unitspawn = Sản xuất -ability.unitspawn.description = Sản xuất đơn vị -ability.shieldregenfield = Tạo khiên nhỏ -ability.shieldregenfield.description = Tạo khiên cho các đơn vị gần đó -ability.movelightning = Phóng điện khi di chuyển -ability.movelightning.description = Giải phóng tia điện khi di chuyển -ability.armorplate = Mảnh giáp -ability.armorplate.description = Giảm sát thương gánh chịu trong khi bắn -ability.shieldarc = Khiên vòng cung -ability.shieldarc.description = Phát một khiên trường lực vòng cung hấp thụ các loại đạn -ability.suppressionfield = Ngăn chặn sửa chữa -ability.suppressionfield.description = Ngăn chặn các công trình sửa chữa -ability.energyfield = Trường điện từ -ability.energyfield.description = Giật điện các kẻ thù gần đó -ability.energyfield.healdescription = Giật điện các kẻ thù gần đó và hồi phục đồng minh -ability.regen = Hồi phục -ability.regen.description = Tự hồi phục theo thời gian -ability.liquidregen = Hấp thụ chất lỏng -ability.liquidregen.description = Hấp thụ chất lỏng để tự hồi phục -ability.spawndeath = Chết sản sinh -ability.spawndeath.description = Sinh ra đơn vị khi chết -ability.liquidexplode = Chết tràn dịch -ability.liquidexplode.description = Tràn chất lỏng khi chết -ability.stat.firingrate = tốc bắn [stat]{0}/giây[lightgray] -ability.stat.regen = [stat]{0}[lightgray] độ bền/giây -ability.stat.shield = [stat]{0}[lightgray] khiên -ability.stat.repairspeed = [stat]{0}/giây[lightgray] tốc độ sửa chữa -ability.stat.slurpheal = [stat]{0}[lightgray] độ bền/đơn vị chất lỏng -ability.stat.cooldown = [stat]{0} giây[lightgray] hồi chiêu -ability.stat.maxtargets = [stat]{0}[lightgray] mục tiêu tối đa -ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] số lượng sửa chữa cùng kiểu -ability.stat.damagereduction = [stat]{0}%[lightgray] giảm sát thương -ability.stat.minspeed = tốc độ tối thiểu [stat]{0} ô/giây[lightgray] -ability.stat.duration = thời hạn [stat]{0} giây[lightgray] -ability.stat.buildtime = thời gian xây [stat]{0} giây[lightgray] ability.forcefield.description = Projects a force shield that absorbs bullets ability.repairfield = Sửa chữa/Xây dựng ability.repairfield.description = Repairs nearby units @@ -1367,7 +1327,6 @@ rules.unitdamagemultiplier = Hệ số sát thương của đơn vị rules.unitcrashdamagemultiplier = Hệ số sát thương của đơn vị khi bị bắn rơi rules.solarmultiplier = Hệ số năng lượng mặt trời rules.unitcapvariable = Căn cứ tăng giới hạn đơn vị -rules.unitpayloadsexplode = Khối hàng mang theo phát nổ cùng đơn vị rules.unitpayloadsexplode = Carried Payloads Explode With The Unit rules.unitcap = Giới hạn đơn vị rules.limitarea = Giới hạn kích thước bản đồ @@ -2332,10 +2291,10 @@ unit.emanate.description = Xây công trình để phòng thủ lõi Acropolis. lst.read = Đọc một số từ bộ nhớ được liên kết. lst.write = Ghi một số vào bộ nhớ được liên kết. lst.print = Thêm văn bản vào bộ nhớ in.\nKhông hiển thị gì cho đến khi sử dụng [accent]Print Flush[]. +lst.format = Thay thế kí tự giữ chỗ tiếp theo ("[accent]@[]") trong bộ đệm văn bản bằng giá trị.\nVí dụ:\n[accent]print "test @"\nformat "example" lst.draw = Thêm một thao tác vào bộ nhớ vẽ.\nKhông hiển thị gì cho đến khi sử dụng [accent]Draw Flush[]. lst.drawflush = Chuyển các thao tác [accent]Draw[] đến màn hình. lst.printflush = Chuyển các thao tác [accent]Print[] đến khối tin nhắn. -lst.format = Thay thế kí tự giữ chỗ tiếp theo ("[accent]@[]") trong bộ đệm văn bản bằng giá trị.\nVí dụ:\n[accent]print "test @"\nformat "example" lst.getlink = Nhận liên kết bộ xử lý theo thứ tự. Bắt đầu từ 0. lst.control = Điều khiển một khối. lst.radar = Định vị các đơn vị trong phạm vi xung quanh một khối. @@ -2566,4 +2525,4 @@ lenum.texturesize = Kích thước của kết cấu bằng ô. Giá trị 0 chi lenum.autoscale = Có chia tỷ lệ điểm đánh dấu tương ứng với mức thu phóng của người chơi hay không. lenum.posi = Vị trí theo chỉ số, dùng cho điểm đánh dấu đường kẻ (line) và bốn điểm (quad) với 0 là vị trí đầu tiên. lenum.uvi = Vị trí của kết cấu trong phạm vi từ 0 đến 1, dùng cho đánh dấu bốn điểm (quad). -lenum.colori = Màu theo chỉ số, dùng cho điểm đánh dấu đường kẻ (line) và bốn điểm (quad) với 0 là màu đầu tiên. \ No newline at end of file +lenum.colori = Màu theo chỉ số, dùng cho điểm đánh dấu đường kẻ (line) và bốn điểm (quad) với 0 là màu đầu tiên. From 45bdceb7fba0425f41a386f88b5afa5a0403785e Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 30 Mar 2024 23:32:19 -0400 Subject: [PATCH 057/348] Update bundle_vi.properties --- core/assets/bundles/bundle_vi.properties | 70 ++++++++++++------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index 393ff981cb..377ce2fff4 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -986,46 +986,46 @@ stat.immunities = Miễn nhiễm stat.healing = Sửa chữa ability.forcefield = Tạo khiên -ability.forcefield.description = Projects a force shield that absorbs bullets +ability.forcefield.description = Phát một khiên trường lực hấp thụ các loại đạn ability.repairfield = Sửa chữa/Xây dựng -ability.repairfield.description = Repairs nearby units +ability.repairfield.description = Sửa chữa các đơn vị gần đó ability.statusfield = Vùng gia tốc -ability.statusfield.description = Applies a status effect to nearby units +ability.statusfield.description = Áp dụng hiệu ứng trạng thái vào các đơn vị gần đó ability.unitspawn = Sản xuất -ability.unitspawn.description = Constructs units +ability.unitspawn.description = Sản xuất đơn vị ability.shieldregenfield = Tạo khiên nhỏ -ability.shieldregenfield.description = Regenerates shields of nearby units +ability.shieldregenfield.description = Tạo khiên cho các đơn vị gần đó ability.movelightning = Phóng điện khi di chuyển -ability.movelightning.description = Releases lightning while moving -ability.armorplate = Armor Plate -ability.armorplate.description = Reduces damage taken while shooting -ability.shieldarc = Khiên Vòng Cung -ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets -ability.suppressionfield = Trường sửa chữa -ability.suppressionfield.description = Stops nearby repair buildings +ability.movelightning.description = Giải phóng tia điện khi di chuyển +ability.armorplate = Mảnh giáp +ability.armorplate.description = Giảm sát thương gánh chịu trong khi bắn +ability.shieldarc = Khiên vòng cung +ability.shieldarc.description = Phát một khiên trường lực vòng cung hấp thụ các loại đạn +ability.suppressionfield = Ngăn chặn sửa chữa +ability.suppressionfield.description = Ngăn chặn các công trình sửa chữa ability.energyfield = Trường điện từ -ability.energyfield.description = Zaps nearby enemies -ability.energyfield.healdescription = Zaps nearby enemies and heals allies -ability.regen = Regeneration -ability.regen.description = Regenerates own health over time -ability.liquidregen = Liquid Absorption -ability.liquidregen.description = Absorbs liquid to heal itself -ability.spawndeath = Death Spawns -ability.spawndeath.description = Releases units on death -ability.liquidexplode = Death Spillage -ability.liquidexplode.description = Spills liquid on death -ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate -ability.stat.regen = [stat]{0}[lightgray] health/sec -ability.stat.shield = [stat]{0}[lightgray] shield -ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed -ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit -ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown -ability.stat.maxtargets = [stat]{0}[lightgray] max targets -ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount -ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction -ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed -ability.stat.duration = [stat]{0} sec[lightgray] duration -ability.stat.buildtime = [stat]{0} sec[lightgray] build time +ability.energyfield.description = Giật điện các kẻ thù gần đó +ability.energyfield.healdescription = Giật điện các kẻ thù gần đó và hồi phục đồng minh +ability.regen = Hồi phục +ability.regen.description = Tự hồi phục theo thời gian +ability.liquidregen = Hấp thụ chất lỏng +ability.liquidregen.description = Hấp thụ chất lỏng để tự hồi phục +ability.spawndeath = Chết sản sinh +ability.spawndeath.description = Sinh ra đơn vị khi chết +ability.liquidexplode = Chết tràn dịch +ability.liquidexplode.description = Tràn chất lỏng khi chết +ability.stat.firingrate = tốc bắn [stat]{0}/giây[lightgray] +ability.stat.regen = [stat]{0}[lightgray] độ bền/giây +ability.stat.shield = [stat]{0}[lightgray] khiên +ability.stat.repairspeed = [stat]{0}/giây[lightgray] tốc độ sửa chữa +ability.stat.slurpheal = [stat]{0}[lightgray] độ bền/đơn vị chất lỏng +ability.stat.cooldown = [stat]{0} giây[lightgray] hồi chiêu +ability.stat.maxtargets = [stat]{0}[lightgray] mục tiêu tối đa +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] số lượng sửa chữa cùng kiểu +ability.stat.damagereduction = [stat]{0}%[lightgray] giảm sát thương +ability.stat.minspeed = tốc độ tối thiểu [stat]{0} ô/giây[lightgray] +ability.stat.duration = thời hạn [stat]{0} giây[lightgray] +ability.stat.buildtime = thời gian xây [stat]{0} giây[lightgray] bar.onlycoredeposit = Chỉ có thể đưa vào căn cứ bar.drilltierreq = Cần máy khoan tốt hơn @@ -1327,7 +1327,7 @@ rules.unitdamagemultiplier = Hệ số sát thương của đơn vị rules.unitcrashdamagemultiplier = Hệ số sát thương của đơn vị khi bị bắn rơi rules.solarmultiplier = Hệ số năng lượng mặt trời rules.unitcapvariable = Căn cứ tăng giới hạn đơn vị -rules.unitpayloadsexplode = Carried Payloads Explode With The Unit +rules.unitpayloadsexplode = Khối hàng mang theo phát nổ cùng đơn vị rules.unitcap = Giới hạn đơn vị rules.limitarea = Giới hạn kích thước bản đồ rules.enemycorebuildradius = Bán kính không xây dựng trong căn cứ của kẻ địch:[lightgray] (ô) From 40cd2fd27605029151a446d0d0bffb6a27fa355c Mon Sep 17 00:00:00 2001 From: FelixDesyatirikov <53256666+FelixDes@users.noreply.github.com> Date: Sun, 31 Mar 2024 23:16:58 +0300 Subject: [PATCH 058/348] Fix pause toggling (#9696) --- server/src/mindustry/server/ServerControl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/mindustry/server/ServerControl.java b/server/src/mindustry/server/ServerControl.java index 8462871083..44e12c76f8 100644 --- a/server/src/mindustry/server/ServerControl.java +++ b/server/src/mindustry/server/ServerControl.java @@ -527,7 +527,7 @@ public class ServerControl implements ApplicationListener{ } boolean pause = arg[0].equals("on"); autoPaused = false; - state.set(state.isPaused() ? State.playing : State.paused); + state.set(pause ? State.paused : State.playing); info(pause ? "Game paused." : "Game unpaused."); }); From b2b29d3900f439c8abe8e782688f9eb662e0bbcf Mon Sep 17 00:00:00 2001 From: c6b2qb5g <160118416+c6b2qb5g@users.noreply.github.com> Date: Mon, 1 Apr 2024 04:52:52 +0700 Subject: [PATCH 059/348] Update servers_v7.json (#9698) * Update servers_v7.json * Update servers_v7.json --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index d62c916a04..430814f6c4 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -295,7 +295,7 @@ }, { "name": "NPTS", - "address": ["77.87.215.102:26863"] + "address": ["94.198.54.132:26863"] }, { "name": "Skywar.VN", From dd083061b5b9a0746c27e3650f3ab71d098a805a Mon Sep 17 00:00:00 2001 From: abcxyzDustry <138785336+abcxyzDustry@users.noreply.github.com> Date: Mon, 1 Apr 2024 04:53:22 +0700 Subject: [PATCH 060/348] Update servers_v7.json (#9697) delete abcxyz ip and replace it with a completely new ip --- servers_v7.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/servers_v7.json b/servers_v7.json index 430814f6c4..56f07af980 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -242,8 +242,8 @@ "address": ["play.3midustry.xyz:6001", "play.3midustry.xyz:6002", "play.3midustry.xyz:6003", "play.3midustry.xyz:6004", "play.3midustry.xyz:6005", "play.3midustry.xyz:6006", "play.3midustry.xyz:6007", "play.3midustry.xyz:6008", "play.3midustry.xyz:6009", "play.3midustry.xyz:6010"] }, { - "name": "ABCXYZ Community", - "address": ["23.88.73.88:23591", "23.88.73.88:23539", "144.76.57.59:14996", "144.76.57.59:16881", "144.76.57.59:13885", "23.88.73.88:32113", "144.76.57.59:9269", "144.76.57.59:24235", "144.76.57.59:24133", "51.81.57.217:24902"] + "name": "Siege Siege", + "address": ["157.90.4.54:25421"] }, { "name": "CroCraft Network", From 3f6deea7197321dab390cd432c7abe706f78d874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=85=AA=E6=A1=A6=E5=A7=AC?= <147405396+LaoHuaJiOfficial@users.noreply.github.com> Date: Mon, 1 Apr 2024 22:30:14 +0800 Subject: [PATCH 061/348] Update bundle_zh_CN.properties(ready to merge) (#9690) * Update bundle_zh_CN.properties(ready to merge) * small fix * translation fix * small fix and done --- core/assets/bundles/bundle_zh_CN.properties | 379 ++++++++++---------- 1 file changed, 191 insertions(+), 188 deletions(-) diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index ddd3e316d9..0ec1c2bb1c 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -152,17 +152,17 @@ mod.incompatiblemod = [red]不兼容 mod.blacklisted = [red]不支持 mod.unmetdependencies = [red]缺少前置模组 mod.erroredcontent = [scarlet]内容错误 -mod.circulardependencies = [red]Circular Dependencies -mod.incompletedependencies = [red]Incomplete Dependencies +mod.circulardependencies = [red]循环依赖 +mod.incompletedependencies = [red]缺失依赖 -mod.requiresversion.details = 所需的最低游戏版本:[accent]{0}[]\n您的游戏版本过低。 这个模组需要更新的游戏版本(通常是beta/alpha版本)才能工作。 -mod.outdatedv7.details = 这个模组与最新版游戏不兼容。 作者必须更新它,并在[accent]mod.json[]文件中写入[accent]minGameVersion: 136[]。 -mod.blacklisted.details = 这个模组由于造成该版本游戏崩溃或其他原因被手动禁用了。 不要使用它。 +mod.requiresversion.details = 所需的最低游戏版本:[accent]{0}[]\n您的游戏版本过低。 此模组需要更新的游戏版本(通常是beta/alpha版本)才能工作。 +mod.outdatedv7.details = 此模组与最新版游戏不兼容。 作者必须更新它,并在[accent]mod.json[]文件中写入[accent]minGameVersion: 136[]。 +mod.blacklisted.details = 此模组由于造成该版本游戏崩溃或其他原因被手动禁用了。 不要使用它。 mod.missingdependencies.details = 缺少前置模组:{0} -mod.erroredcontent.details = 这个模组在游戏加载时发生了错误。 请通知模组作者修复它。 -mod.circulardependencies.details = This mod has dependencies that depends on each other. -mod.incompletedependencies.details = This mod is unable to be loaded due to invalid or missing dependencies: {0}. -mod.requiresversion = Requires game version: [red]{0} +mod.erroredcontent.details = 此模组在游戏加载时发生了错误。 请通知模组作者修复它。 +mod.circulardependencies.details = 此模组与其他模组相互依赖。 +mod.incompletedependencies.details = 由于依赖项无效或缺失,此模组无法加载:{0}. +mod.requiresversion = 需要游戏版本: [red]{0} mod.errors = 读取内容时发生错误。 mod.noerrorplay = [scarlet]您的模组发生了错误。 []禁用相关模组或修复错误后才能进入游戏。 @@ -258,19 +258,19 @@ trace = 跟踪玩家 trace.playername = 玩家名称:[accent]{0} trace.ip = IP地址:[accent]{0} trace.id = 玩家 ID:[accent]{0} -trace.language = Language: [accent]{0} +trace.language = 语言: [accent]{0} trace.mobile = 移动客户端:[accent]{0} trace.modclient = 自定义客户端:[accent]{0} trace.times.joined = 进入服务器次数: [accent]{0} trace.times.kicked = 踢出服务器次数: [accent]{0} -trace.ips = IPs: -trace.names = Names: +trace.ips = 曾用IP: +trace.names = 曾用名: invalidid = 无效的客户端ID!提交一个错误报告。 -player.ban = Ban -player.kick = Kick -player.trace = Trace -player.admin = Toggle Admin -player.team = Change Team +player.ban = 封禁 +player.kick = 踢出 +player.trace = 追朔 +player.admin = 切换管理员 +player.team = 改变队伍 server.bans = 黑名单 server.bans.none = 没有被封禁的玩家! server.admins = 管理员 @@ -287,8 +287,8 @@ confirmkick = 确定踢出玩家“{0}[white]”? confirmunban = 确定解封这名玩家? confirmadmin = 确定给予玩家“{0}[white]”管理员权限? confirmunadmin = 确定收回玩家“{0}[white]”的管理员权限? -votekick.reason = Vote-Kick Reason -votekick.reason.message = Are you sure you want to vote-kick "{0}[white]"?\nIf yes, please enter the reason: +votekick.reason = 投票踢出理由 +votekick.reason.message = 确定投票踢出玩家"{0}[white]"?\n如果是,请输入理由: joingame.title = 加入游戏 joingame.ip = 地址: disconnect = 已断开连接 @@ -306,7 +306,7 @@ server.invalidport = 无效的端口! server.error = [scarlet]创建服务器错误。 save.new = 新存档 save.overwrite = 确定要覆盖这个存档吗? -save.nocampaign = Individual save files from the campaign cannot be imported. +save.nocampaign = 无法导入战役中的单个保存文件。 overwrite = 覆盖 save.none = 没有找到存档! savefail = 保存失败! @@ -344,23 +344,23 @@ open = 打开 customize = 自定义规则 cancel = 取消 command = 指挥 -command.queue = [lightgray][Queuing] +command.queue = [lightgray][排队中] command.mine = 挖矿 command.repair = 维修 command.rebuild = 重建 command.assist = 协助建造 command.move = 移动 -command.boost = Boost -command.enterPayload = Enter Payload Block -command.loadUnits = Load Units -command.loadBlocks = Load Blocks -command.unloadPayload = Unload Payload -stance.stop = Cancel Orders -stance.shoot = Stance: Shoot -stance.holdfire = Stance: Hold Fire -stance.pursuetarget = Stance: Pursue Target -stance.patrol = Stance: Patrol Path -stance.ram = Stance: Ram\n[lightgray]Straight line movement, no pathfinding +command.boost = 助推 +command.enterPayload = 进入载荷建筑 +command.loadUnits = 拾取单位 +command.loadBlocks = 拾取建筑 +command.unloadPayload = 卸载载荷 +stance.stop = 取消指令 +stance.shoot = 姿态: 射击 +stance.holdfire = 姿态: 停火 +stance.pursuetarget = 姿态: 追逐目标 +stance.patrol = 姿态: 路径巡逻 +stance.ram = 姿态: 冲锋\n[lightgray]径直移动,不进行寻路。 openlink = 打开链接 copylink = 复制链接 back = 返回 @@ -386,8 +386,8 @@ pausebuilding = 按[accent][[{0}][]键暂停建造 resumebuilding = 按[scarlet][[{0}][]键恢复建造 enablebuilding = 按[scarlet][[{0}][]键启用建造 showui = UI已隐藏\n按[accent][[{0}][]键显示UI -commandmode.name = [accent]Command Mode -commandmode.nounits = [no units] +commandmode.name = [accent]指挥模式 +commandmode.nounits = [无单位] wave = [accent]第{0}波 wave.cap = [accent]波次 {0}/{1} wave.waiting = [lightgray]下一波倒计时:{0}秒 @@ -441,7 +441,7 @@ editor.waves = 波次 editor.rules = 规则 editor.generation = 生成 editor.objectives = 目标 -editor.locales = Locale Bundles +editor.locales = 本地化语言包 editor.ingame = 游戏内编辑 editor.playtest = 游戏内测试 editor.publish.workshop = 上传到创意工坊 @@ -473,7 +473,7 @@ waves.max = 最大单位数 waves.guardian = Boss waves.preview = 预览 waves.edit = 编辑… -waves.random = Random +waves.random = 随机 waves.copy = 复制到剪贴板 waves.load = 从剪贴板读取 waves.invalid = 剪贴板中的波次信息无效。 @@ -484,12 +484,12 @@ waves.sort.reverse = 反向排序 waves.sort.begin = 出场顺序 waves.sort.health = 生命值 waves.sort.type = 类型 -waves.search = Search waves... -waves.filter = Unit Filter +waves.search = 搜索波次... +waves.filter = 单位过滤器 waves.units.hide = 全部隐藏 waves.units.show = 全部显示 -#these are intentionally in lower case(中文无关) +#these are intentionally in lower case wavemode.counts = 数目 wavemode.totals = 总数 wavemode.health = 生命值 @@ -498,7 +498,7 @@ editor.default = [lightgray]<默认> details = 详情… edit = 编辑… variables = 变量 -logic.globals = Built-in Variables +logic.globals = 内置变量 editor.name = 名称: editor.spawn = 生成单位 editor.removeunit = 移除单位 @@ -510,7 +510,7 @@ editor.errorlegacy = 此地图太旧了,旧的地图格式已不再支持。 editor.errornot = 这不是地图文件。 editor.errorheader = 此地图文件无效或已损坏。 editor.errorname = 地图没有定义名称。 加载的可能是存档文件? -editor.errorlocales = Error reading invalid locale bundles. +editor.errorlocales = 读取无效本地化语言包时出错。 editor.update = 更新 editor.randomize = 重新生成 editor.moveup = 上移 @@ -522,7 +522,7 @@ editor.sectorgenerate = 生成区块 editor.resize = 改变尺寸 editor.loadmap = 载入地图 editor.savemap = 保存地图 -editor.savechanges = [scarlet]You have unsaved changes!\n\n[]Do you want to save them? +editor.savechanges = [scarlet]您有未保存的更改!\n\n[]您想要保存他们吗? editor.saved = 已保存! editor.save.noname = 您还没有指定地图的名称!在“地图信息”菜单里设置一个名称。 editor.save.overwrite = 您正试图覆盖一张内置地图!在“地图信息”菜单里重新设置一个其他的名称。 @@ -561,8 +561,8 @@ toolmode.eraseores = 擦除矿脉 toolmode.eraseores.description = 仅擦除矿脉,不影响其他物体。 toolmode.fillteams = 填充队伍 toolmode.fillteams.description = 不再填充方块,而是填充队伍颜色。 -toolmode.fillerase = Fill Erase -toolmode.fillerase.description = Erase blocks of the same type. +toolmode.fillerase = 擦除同类 +toolmode.fillerase.description = 擦除同种种类的方块。 toolmode.drawteams = 绘制队伍 toolmode.drawteams.description = 不再绘制方块,而是绘制队伍颜色。 #未使用 @@ -610,23 +610,23 @@ filter.option.floor2 = 内层地形 filter.option.threshold2 = 内层比例 filter.option.radius = 半径 filter.option.percentile = 百分比 -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) -locales.deletelocale = Are you sure you want to delete this locale bundle? -locales.applytoall = Apply Changes To All Locales -locales.addtoother = Add To Other Locales -locales.rollback = Rollback to last applied -locales.filter = Property filter -locales.searchname = Search name... -locales.searchvalue = Search value... -locales.searchlocale = Search locale... -locales.byname = By name -locales.byvalue = By value -locales.showcorrect = Show properties that are present in all locales and have unique values everywhere -locales.showmissing = Show properties that are missing in some locales -locales.showsame = Show properties that have same values in different locales -locales.viewproperty = View in all locales -locales.viewing = Viewing property "{0}" -locales.addicon = Add Icon +locales.info = 在这里,您可以为特定语言添加本地化语言包到您的地图中。在本地化语言包中,每个文本属性都有一个名称和一个值。这些文本属性可以由世界处理器和游戏目标使用它们的名称。它们支持文本格式化(用实际值替换占位符)。\n\n[cyan]示例文本属性:\n[]名称: [accent]timer[]\值: [accent]示例计时器, 剩余时间: @[]\n\n[cyan]用法:\n[]将其设置为目标的文本: [accent]@timer\n\n[]在世界处理器中打印它:\n[accent]localeprint "timer"\n格式化时间\n[gray](时间是一个单独计算的变量) +locales.deletelocale = 您确定要删除该本地化语言包吗? +locales.applytoall = 将更改应用于所有本地化语言包 +locales.addtoother = 添加到其他本地化语言包 +locales.rollback = 回滚到上次应用的状态 +locales.filter = 文本属性过滤器 +locales.searchname = 搜索名称... +locales.searchvalue = 搜索值... +locales.searchlocale = 搜索本地化... +locales.byname = 按名称 +locales.byvalue = 按值 +locales.showcorrect = 显示所有本地化语言包中存在并且在所有地方具有唯一值的文本属性 +locales.showmissing = 显示在某些本地化语言包中缺失的文本属性 +locales.showsame = 显示在不同本地化语言包中具有相同值的文本属性 +locales.viewproperty = 在所有本地化语言包中查看 +locales.viewing = 查看文本属性 "{0}" +locales.addicon = 添加图标 width = 宽度: height = 高度: @@ -679,11 +679,11 @@ objective.commandmode.name = 指挥模式 objective.flag.name = 标签 marker.shapetext.name = 带形状文本 -marker.point.name = Point +marker.point.name = 点 marker.shape.name = 形状 marker.text.name = 文本 -marker.line.name = Line -marker.quad.name = Quad +marker.line.name = 线 +marker.quad.name = 四边形 marker.background = 背景 marker.outline = 轮廓 @@ -771,8 +771,8 @@ sector.curlost = 区块已丢失 sector.missingresources = [scarlet]建造核心所需资源不足 sector.attacked = 区块[accent]{0}[white]受到攻击! sector.lost = 区块[accent]{0}[white]已丢失! -sector.capture = Sector [accent]{0}[white]Captured! -sector.capture.current = Sector Captured! +sector.capture = 区块[accent]{0}[white]已占领! +sector.capture.current = 区块已占领! sector.changeicon = 更改图标 sector.noswitch.title = 无法切换区块 sector.noswitch = 你无法在当前区块遭受攻击时切换区块。\n\n区块:[accent]{0}[]位于[accent]{1}[] @@ -947,7 +947,7 @@ stat.repairspeed = 修理速度 stat.weapons = 武器 stat.bullet = 子弹 stat.moduletier = 模块等级 -stat.unittype = Unit Type +stat.unittype = 单位类型 stat.speedincrease = 提速 stat.range = 范围 stat.drilltier = 可钻探矿物 @@ -994,46 +994,47 @@ stat.immunities = 免疫 stat.healing = 治疗 ability.forcefield = 力墙场 -ability.forcefield.description = Projects a force shield that absorbs bullets +ability.forcefield.description = 投射一个能吸收子弹的力场护盾 ability.repairfield = 修复场 -ability.repairfield.description = Repairs nearby units +ability.repairfield.description = 修复附近的单位 ability.statusfield = 状态场 -ability.statusfield.description = Applies a status effect to nearby units -ability.unitspawn = 单位工厂 -ability.unitspawn.description = Constructs units +ability.statusfield.description = 对附近的单位施加状态效果 +ability.unitspawn = 单位生成 +ability.unitspawn.description = 建造单位 ability.shieldregenfield = 护盾再生场 -ability.shieldregenfield.description = Regenerates shields of nearby units +ability.shieldregenfield.description = 再生附近单位的护盾 ability.movelightning = 闪电助推器 -ability.movelightning.description = Releases lightning while moving -ability.armorplate = Armor Plate -ability.armorplate.description = Reduces damage taken while shooting +ability.movelightning.description = 移动时释放闪电 +ability.armorplate = 装甲板 +ability.armorplate.description = 在射击时减少受到的伤害 ability.shieldarc = 弧形护盾 -ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets +ability.shieldarc.description = 投射一个弧形的力场护盾,能吸收子弹 ability.suppressionfield = 修复压制场 -ability.suppressionfield.description = Stops nearby repair buildings -ability.energyfield = 能量场: -ability.energyfield.description = Zaps nearby enemies -ability.energyfield.healdescription = Zaps nearby enemies and heals allies -ability.regen = Regeneration -ability.regen.description = Regenerates own health over time -ability.liquidregen = Liquid Absorption -ability.liquidregen.description = Absorbs liquid to heal itself -ability.spawndeath = Death Spawns -ability.spawndeath.description = Releases units on death -ability.liquidexplode = Death Spillage -ability.liquidexplode.description = Spills liquid on death -ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate -ability.stat.regen = [stat]{0}[lightgray] health/sec -ability.stat.shield = [stat]{0}[lightgray] shield -ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed -ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit -ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown -ability.stat.maxtargets = [stat]{0}[lightgray] max targets -ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount -ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction -ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed -ability.stat.duration = [stat]{0} sec[lightgray] duration -ability.stat.buildtime = [stat]{0} sec[lightgray] build time +ability.suppressionfield.description = 使附近的修复建筑停止工作 +ability.energyfield = 能量场 +ability.energyfield.description = 对附近的敌人释放电击 +ability.energyfield.healdescription = 对附近的敌人释放电击,并治疗友方 +ability.regen = 再生 +ability.regen.description = 随着时间的推移恢复自己的生命值 +ability.liquidregen = 液体吸收 +ability.liquidregen.description = 吸收液体以治疗自身 +ability.spawndeath = 死亡产生单位 +ability.spawndeath.description = 死亡时释放单位 +ability.liquidexplode = 死亡溢液 +ability.liquidexplode.description = 死亡时释放液体 +ability.stat.firingrate = [stat]{0}/秒[lightgray] 射速 +ability.stat.regen = [stat]{0}/秒[lightgray] 生命恢复速度 +ability.stat.shield = [stat]{0}[lightgray] 护盾 +ability.stat.repairspeed = [stat]{0}/秒[lightgray] 修复速度 +ability.stat.slurpheal = [stat]{0}[lightgray] 生命/液体单位 +ability.stat.cooldown = [stat]{0} 秒[lightgray] 冷却时间 +ability.stat.maxtargets = [stat]{0}[lightgray] 最大目标数 +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] 同类型修复量 +ability.stat.damagereduction = [stat]{0}%[lightgray] 伤害减免 +ability.stat.minspeed = [stat]{0} 格/秒[lightgray] 最低速度 +ability.stat.duration = [stat]{0} 秒[lightgray] 持续时间 +ability.stat.buildtime = [stat]{0} 秒[lightgray] 建造时间 + bar.onlycoredeposit = 仅核心可丢入资源 bar.drilltierreq = 需要更高级的钻头 @@ -1073,9 +1074,9 @@ bullet.splashdamage = [stat]{0}[lightgray]范围伤害~[stat] {1}[lightgray]格 bullet.incendiary = [stat]燃烧 bullet.homing = [stat]追踪 bullet.armorpierce = [stat]穿甲 -bullet.maxdamagefraction = [stat]{0}%[lightgray] damage limit -bullet.suppression = [stat]{0} sec[lightgray] repair suppression ~ [stat]{1}[lightgray] tiles -bullet.interval = [stat]{0}/sec[lightgray] interval bullets: +bullet.maxdamagefraction = [stat]{0}%[lightgray] 伤害上限 +bullet.suppression = [stat]{0}秒[lightgray] 修复压制 ~ [stat]{1}[lightgray] 格 +bullet.interval = [stat]{0}/秒[lightgray] 分裂子弹: bullet.frags = [stat]{0}[lightgray]x分裂子弹: bullet.lightning = [stat]{0}[lightgray]x闪电~[stat]{1}[lightgray]伤害 bullet.buildingdamage = [stat]{0}%[lightgray]对建筑伤害 @@ -1129,7 +1130,7 @@ setting.backgroundpause.name = 游戏在后台时自动暂停 setting.buildautopause.name = 自动暂停建造 setting.doubletapmine.name = 双击采矿 setting.commandmodehold.name = 长按保持指挥模式 -setting.distinctcontrolgroups.name = Limit One Control Group Per Unit +setting.distinctcontrolgroups.name = 每单位限制一个编队 setting.modcrashdisable.name = 游戏启动崩溃后禁用模组 setting.animatedwater.name = 动态液体 setting.animatedshields.name = 动态力场 @@ -1176,14 +1177,14 @@ setting.position.name = 显示玩家坐标 setting.mouseposition.name = 显示鼠标坐标 setting.musicvol.name = 音乐音量 setting.atmosphere.name = 显示行星大气层 -setting.drawlight.name = Draw Darkness/Lighting +setting.drawlight.name = 绘制阴影/光照 setting.ambientvol.name = 环境音量 setting.mutemusic.name = 禁用音乐 setting.sfxvol.name = 音效音量 setting.mutesound.name = 禁用音效 setting.crashreport.name = 发送匿名的崩溃报告 setting.savecreate.name = 自动创建存档 -setting.steampublichost.name = Public Game Visibility +setting.steampublichost.name = 公共游戏可见性 setting.playerlimit.name = 玩家数量限制 setting.chatopacity.name = 聊天界面不透明度 setting.lasersopacity.name = 电力连接线不透明度 @@ -1193,8 +1194,8 @@ setting.showweather.name = 显示天气效果 setting.hidedisplays.name = 不显示逻辑绘图 setting.macnotch.name = 立陶宛語 setting.macnotch.description = 需要重新启动 -steam.friendsonly = Friends Only -steam.friendsonly.tooltip = Whether only Steam friends will be able to join your game.\nUnchecking this box will make your game public - anyone can join. +steam.friendsonly = 仅限好友 +steam.friendsonly.tooltip = 是否只有 Steam 好友才能加入您的游戏。\n取消选中此选项将使您的游戏公开 - 任何人都可以加入。 public.beta = 请注意,测试版的游戏不能公开可见。 uiscale.reset = UI缩放比例已更改。\n点击“确定”接受更改。\n[accent]{0}[]秒后[scarlet]将自动退出并还原设置。 uiscale.cancel = 取消并退出 @@ -1203,7 +1204,7 @@ keybind.title = 重新绑定按键 keybinds.mobile = [scarlet]除了基本的移动以外,大多数按键绑定在移动设备上无效。 category.general.name = 常规 category.view.name = 视图 -category.command.name = Unit Command +category.command.name = 单位指挥 category.multiplayer.name = 多人游戏 category.blocks.name = 建筑选择 placement.blockselectkeys = \n[lightgray]按键:[{0}, @@ -1221,23 +1222,23 @@ keybind.mouse_move.name = 单位跟随鼠标 keybind.pan.name = 鼠标控制镜头 keybind.boost.name = 启动助推 keybind.command_mode.name = 指挥模式 -keybind.command_queue.name = Unit Command Queue -keybind.create_control_group.name = Create Control Group -keybind.cancel_orders.name = Cancel Orders -keybind.unit_stance_shoot.name = Unit Stance: Shoot -keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire -keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target -keybind.unit_stance_patrol.name = Unit Stance: Patrol -keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.command_queue.name = 单位指挥队列 +keybind.create_control_group.name = 创建操控队伍 +keybind.cancel_orders.name = 取消指令 +keybind.unit_stance_shoot.name = 单位姿态:射击 +keybind.unit_stance_hold_fire.name = 单位姿态:停火 +keybind.unit_stance_pursue_target.name = 单位姿态:追逐目标 +keybind.unit_stance_patrol.name = 单位姿态:巡逻 +keybind.unit_stance_ram.name = 单位姿态:冲锋 +keybind.unit_command_move = 单位指挥:移动 +keybind.unit_command_repair = 单位指挥:修复 +keybind.unit_command_rebuild = 单位指挥:重建 +keybind.unit_command_assist = 单位指挥:协助 +keybind.unit_command_mine = 单位指挥:采矿 +keybind.unit_command_boost = 单位指挥:助推 +keybind.unit_command_load_units = 单位指挥:拾取单位 +keybind.unit_command_load_blocks = 单位指挥:拾取建筑 +keybind.unit_command_unload_payload = 单位指挥:卸载载荷 keybind.rebuild_select.name = 重建建筑 keybind.schematic_select.name = 框选建筑 keybind.schematic_menu.name = 蓝图目录 @@ -1301,12 +1302,12 @@ mode.pvp.description = 与其他玩家对战。 \n[gray]需要地图中有至少 mode.attack.name = 进攻 mode.attack.description = 摧毁敌人的基地。 \n[gray]需要地图中有敌方队伍的核心。 mode.custom = 自定义模式 -rules.invaliddata = Invalid clipboard data. +rules.invaliddata = 无效剪贴板数据。 rules.hidebannedblocks = 隐藏禁用的建筑 rules.infiniteresources = 无限资源 rules.onlydepositcore = 仅核心可放入资源 -rules.derelictrepair = Allow Derelict Block Repair +rules.derelictrepair = 允许修复残骸建筑 rules.reactorexplosions = 反应堆爆炸 rules.coreincinerates = 核心焚烧 rules.disableworldprocessors = 禁用世界处理器 @@ -1315,8 +1316,8 @@ rules.wavetimer = 波次计时器 rules.wavesending = 波次可跳波 rules.waves = 波次 rules.attack = 进攻模式 -rules.buildai = Base Builder AI -rules.buildaitier = Builder AI Tier +rules.buildai = 基础建筑者 AI +rules.buildaitier = 建筑者 AI 等级 rules.rtsai = RTS AI rules.rtsminsquadsize = 最小部队规模 rules.rtsmaxsquadsize = 最大部队规模 @@ -1332,10 +1333,10 @@ rules.unitbuildspeedmultiplier = 单位生产速度倍率 rules.unitcostmultiplier = 单位生产花费倍率 rules.unithealthmultiplier = 单位生命倍率 rules.unitdamagemultiplier = 单位伤害倍率 -rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier +rules.unitcrashdamagemultiplier = 单位坠毁伤害倍率 rules.solarmultiplier = 太阳能发电倍率 rules.unitcapvariable = 核心可增加单位上限 -rules.unitpayloadsexplode = Carried Payloads Explode With The Unit +rules.unitpayloadsexplode = 单位携带载荷与单位一起爆炸 rules.unitcap = 基础单位上限 rules.limitarea = 限制地图有效区域 rules.enemycorebuildradius = 敌方核心不可建造区域半径:[lightgray](格) @@ -1345,7 +1346,7 @@ rules.buildcostmultiplier = 建造花费倍率 rules.buildspeedmultiplier = 建造速度倍率 rules.deconstructrefundmultiplier = 拆除返还倍率 rules.waitForWaveToEnd = 等待波次结束 -rules.wavelimit = Map Ends After Wave +rules.wavelimit = 地图在有限波次后结束 rules.dropzoneradius = 敌人出生点禁区大小:[lightgray](格) rules.unitammo = 单位有弹药限制 rules.enemyteam = 敌方队伍 @@ -2339,8 +2340,8 @@ lst.getblock = 获取任意位置的地块数据 lst.setblock = 设置任意位置的地块数据 lst.spawnunit = 在指定位置生成单位 lst.applystatus = 添加或清除单位的一个状态效果 -lst.weathersense = Check if a type of weather is active. -lst.weatherset = Set the current state of a type of weather. +lst.weathersense = 检查特定种类的天气当前是否启用。 +lst.weatherset = 设置当前状态为特定类型天气。 lst.spawnwave = 在任意位置生成一波敌人\n并不记录在波数计数器中 lst.explosion = 在某个位置生成爆炸 lst.setrate = 在指令/时间刻的时间下设置处理器处理速度 @@ -2349,50 +2350,51 @@ lst.packcolor = 将[0,1]范围内的RGBA分量整合成单个数字,用于绘 lst.setrule = 设置地图规则 lst.flushmessage = 在屏幕中央投影文字缓存区的内容\n会等待上一个文字显示结束 lst.cutscene = 控制玩家游戏视角 -lst.setflag = 设置一个可以被所有处理器读取的全局flag -lst.getflag = 检查是否设置了全局flag -lst.setprop = Sets a property of a unit or building. -lst.effect = Create a particle effect. -lst.sync = Sync a variable across the network.\nOnly invoked 10 times a second at most. -lst.makemarker = Create a new logic marker in the world.\nAn ID to identify this marker must be provided.\nMarkers currently limited to 20,000 per world. -lst.setmarker = Set a property for a marker.\nThe ID used must be the same as in the Make Marker instruction. -lst.localeprint = Add map locale property value to the text buffer.\nTo set map locale bundles in map editor, check [accent]Map Info > Locale Bundles[].\nIf client is a mobile device, tries to print a property ending in ".mobile" first. +lst.setflag = 设置一个可以被所有处理器读取的全局标志。 +lst.getflag = 检查是否设置了全局标志。 +lst.setprop = 设置单位或建筑物的属性。 +lst.effect = 创建一个粒子效果。 +lst.sync = 在网络中同步一个变量。\n最多每秒调用10次。 +lst.makemarker = 在世界中创建一个新的逻辑标记。\n必须提供一个用于标识此标记的ID。\n目前每个世界限制最多20000个标记。 +lst.setmarker = 为标记设置属性。\n使用的ID必须与制作标记指令中的相同。 +lst.localeprint = 将地图本地化文本属性值添加到文本缓冲区中。\n要在地图编辑器中设置地图本地化包,请检查 [accent]地图信息 > 本地化包[]。\n如果客户端是移动设备,则尝试首先打印以 ".mobile" 结尾的属性。 lglobal.false = 0 lglobal.true = 1 lglobal.null = null -lglobal.@pi = The mathematical constant pi (3.141...) -lglobal.@e = The mathematical constant e (2.718...) -lglobal.@degToRad = Multiply by this number to convert degrees to radians -lglobal.@radToDeg = Multiply by this number to convert radians to degrees -lglobal.@time = Playtime of current save, in milliseconds -lglobal.@tick = Playtime of current save, in ticks (1 second = 60 ticks) -lglobal.@second = Playtime of current save, in seconds -lglobal.@minute = Playtime of current save, in minutes -lglobal.@waveNumber = Current wave number, if waves are enabled -lglobal.@waveTime = Countdown timer for waves, in seconds -lglobal.@mapw = Map width in tiles -lglobal.@maph = Map height in tiles -lglobal.sectionMap = Map -lglobal.sectionGeneral = General -lglobal.sectionNetwork = Network/Clientside [World Processor Only] -lglobal.sectionProcessor = Processor -lglobal.sectionLookup = Lookup -lglobal.@this = The logic block executing the code -lglobal.@thisx = X coordinate of block executing the code -lglobal.@thisy = Y coordinate of block executing the code -lglobal.@links = Total number of blocks linked to this processors -lglobal.@ipt = Execution speed of the processor in instructions per tick (60 ticks = 1 second) -lglobal.@unitCount = Total number of types of unit content in the game; used with the lookup instruction -lglobal.@blockCount = Total number of types of block content in the game; used with the lookup instruction -lglobal.@itemCount = Total number of types of item content in the game; used with the lookup instruction -lglobal.@liquidCount = Total number of types of liquid content in the game; used with the lookup instruction -lglobal.@server = True if the code is running on a server or in singleplayer, false otherwise -lglobal.@client = True if the code is running on a client connected to a server -lglobal.@clientLocale = Locale of the client running the code. For example: en_US -lglobal.@clientUnit = Unit of client running the code -lglobal.@clientName = Player name of client running the code -lglobal.@clientTeam = Team ID of client running the code -lglobal.@clientMobile = True is the client running the code is on mobile, false otherwise +lglobal.@pi = 数学常数 pi (3.141...) +lglobal.@e = 数学常数 e (2.718...) +lglobal.@degToRad = 将角度制转换为弧度制 +lglobal.@radToDeg = 将弧度制转换为角度制 +lglobal.@time = 当前保存的游戏时间,以毫秒为单位 +lglobal.@tick = 当前保存的游戏时间,以tick为单位(1秒 = 60 tick) +lglobal.@second = 当前保存的游戏时间,以秒为单位 +lglobal.@minute = 当前保存的游戏时间,以分钟为单位 +lglobal.@waveNumber = 如果启用了波次,则为当前波次编号 +lglobal.@waveTime = 波次的倒计时计时器,以秒为单位 +lglobal.@mapw = 地图宽度(单位:格) +lglobal.@maph = 地图高度(单位:格) +lglobal.sectionMap = 地图 +lglobal.sectionGeneral = 通用 +lglobal.sectionNetwork = 网络/客户端 [仅限世界处理器] +lglobal.sectionProcessor = 处理器 +lglobal.sectionLookup = 查找 +lglobal.@this = 执行代码的逻辑块 +lglobal.@thisx = 执行代码的逻辑块的 X 坐标 +lglobal.@thisy = 执行代码的逻辑块的 Y 坐标 +lglobal.@links = 连接到此处理器的总块数 +lglobal.@ipt = 处理器每 tick 的执行速度(每秒 60 tick) +lglobal.@unitCount = 游戏中单位内容的类型总数;与查找指令一起使用 +lglobal.@blockCount = 游戏中块内容的类型总数;与查找指令一起使用 +lglobal.@itemCount = 游戏中物品内容的类型总数;与查找指令一起使用 +lglobal.@liquidCount = 游戏中液体内容的类型总数;与查找指令一起使用 +lglobal.@server = 如果代码正在服务器上运行或单人游戏中运行,则为真,否则为假 +lglobal.@client = 如果代码正在连接到服务器的客户端上运行,则为真 +lglobal.@clientLocale = 运行代码的客户端的区域设置。例如:en_US +lglobal.@clientUnit = 运行代码的客户端的单位 +lglobal.@clientName = 运行代码的客户端的玩家名称 +lglobal.@clientTeam = 运行代码的客户端的团队 ID +lglobal.@clientMobile = 如果运行代码的客户端在移动设备上,则为真,否则为假 + logic.nounitbuild = [red]此处不允许处理器操控单位去建设 @@ -2408,7 +2410,7 @@ laccess.dead = 单位或建筑是否已被摧毁或者已失效 laccess.controlled = 若单位的控制方是处理器,返回[accent]@ctrlProcessor[]\n若单位/建筑由玩家控制,返回[accent]@ctrlPlayer[]\n若单位在编队中,返回[accent]@ctrlFormation[]\n其他情况,返回0 laccess.progress = 进度,0到1之间的数值。 \n返回工厂生产、 炮塔装填,或者建筑建造的进度 laccess.speed = 单位的最高速度(格/秒) -laccess.id = ID of a unit/block/item/liquid.\nThis is the inverse of the lookup operation. +laccess.id = 单位/块/物品/液体的ID。\n这是 Lookup 的反向操作。 lcategory.unknown = 未知 lcategory.unknown.description = 未分类的指令 @@ -2436,7 +2438,7 @@ graphicstype.poly = 绘制实心正多边形 graphicstype.linepoly = 绘制正多边形轮廓 graphicstype.triangle = 绘制实心三角形 graphicstype.image = 画出某个游戏内容的图像\n例如[accent]@router[]或者[accent]@dagger[] -graphicstype.print = Draws text from the print buffer.\nClears the print buffer. +graphicstype.print = 从打印缓冲区绘制文本。\n清除打印缓冲区。 lenum.always = 无条件跳转 lenum.idiv = 整数除法,返回不带小数的商 @@ -2456,7 +2458,7 @@ lenum.xor = 按位异或 lenum.min = 取较小值 lenum.max = 取较大值 lenum.angle = 返回向量的辐角(角度制) -lenum.anglediff = Absolute distance between two angles in degrees. +lenum.anglediff = 返回两个角度之间的绝对距离(角度制)。 lenum.len = 返回向量的长度 lenum.sin = 正弦(角度制) @@ -2531,7 +2533,7 @@ lenum.unbind = 停用单位的逻辑控制\n恢复常规AI lenum.move = 移动到某个位置 lenum.approach = 靠近某个位置至一定的距离内 lenum.pathfind = 寻路移动至敌人出生点 -lenum.autopathfind = Automatically pathfinds to the nearest enemy core or drop point.\nThis is the same as standard wave enemy pathfinding. +lenum.autopathfind = "自动寻找最近的敌方核心或敌人生成点。\n这与波次中的敌人寻路相同。" lenum.target = 向某个位置瞄准/射击 lenum.targetp = 根据提前量向某个目标瞄准/射击 lenum.itemdrop = 将携带的物品放入一座建筑 @@ -2545,10 +2547,11 @@ lenum.build = 建造建筑 lenum.getblock = 获取某个坐标处的建筑及其类型\n坐标需要在单位的感知范围内\n无建筑的地面返回[accent]@air[],墙壁返回[accent]@solid[] lenum.within = 检查单位是否接近了某个位置 lenum.boost = 开始/停止助推 -lenum.flushtext = Flush print buffer's content to marker, if applicable.\nIf fetch is set to true, tries to fetch properties from map locale bundle or game's bundle. -lenum.texture = Texture name straight from game's texture atlas (using kebab-case naming style).\nIf printFlush is set to true, consumes text buffer content as text argument. -lenum.texturesize = Size of texture in tiles. Zero value scales marker width to original texture's size. -lenum.autoscale = Whether to scale marker corresponding to player's zoom level. -lenum.posi = Indexed position, used for line and quad markers with index zero being the first position. -lenum.uvi = Texture's position ranging from zero to one, used for quad markers. -lenum.colori = Indexed position, used for line and quad markers with index zero being the first color. +lenum.flushtext = 如果适用的话,将打印缓冲区的内容刷新到标记。\n如果 fetch 设置为 true,则尝试从地图本地化包或游戏的包中获取属性。 +lenum.texture = 直接来自游戏纹理图集的纹理名称(使用 kebab-case 命名风格)。\n如果 printFlush 设置为 true,则将文本缓冲区内容作为文本参数消耗。 +lenum.texturesize = 纹理的大小(格)。零值将标记宽度缩放为原始纹理的大小。 +lenum.autoscale = 是否根据玩家的缩放级别缩放标记。 +lenum.posi = 索引位置,用于线和四边形标记,索引零表示第一个位置。 +lenum.uvi = 纹理的位置范围从零到一,用于四边形标记。 +lenum.colori = 索引位置,用于线和四边形标记,索引零表示第一个颜色。 + From 1f5d8b1f045cbace88d81de4b3b0b6323f723e0a Mon Sep 17 00:00:00 2001 From: Github Actions Date: Mon, 1 Apr 2024 14:30:58 +0000 Subject: [PATCH 062/348] Automatic bundle update --- core/assets/bundles/bundle_zh_CN.properties | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index 0ec1c2bb1c..2a74a48802 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -610,7 +610,7 @@ filter.option.floor2 = 内层地形 filter.option.threshold2 = 内层比例 filter.option.radius = 半径 filter.option.percentile = 百分比 -locales.info = 在这里,您可以为特定语言添加本地化语言包到您的地图中。在本地化语言包中,每个文本属性都有一个名称和一个值。这些文本属性可以由世界处理器和游戏目标使用它们的名称。它们支持文本格式化(用实际值替换占位符)。\n\n[cyan]示例文本属性:\n[]名称: [accent]timer[]\值: [accent]示例计时器, 剩余时间: @[]\n\n[cyan]用法:\n[]将其设置为目标的文本: [accent]@timer\n\n[]在世界处理器中打印它:\n[accent]localeprint "timer"\n格式化时间\n[gray](时间是一个单独计算的变量) +locales.info = 在这里,您可以为特定语言添加本地化语言包到您的地图中。在本地化语言包中,每个文本属性都有一个名称和一个值。这些文本属性可以由世界处理器和游戏目标使用它们的名称。它们支持文本格式化(用实际值替换占位符)。\n\n[cyan]示例文本属性:\n[]名称: [accent]timer[]值: [accent]示例计时器, 剩余时间: @[]\n\n[cyan]用法:\n[]将其设置为目标的文本: [accent]@timer\n\n[]在世界处理器中打印它:\n[accent]localeprint "timer"\n格式化时间\n[gray](时间是一个单独计算的变量) locales.deletelocale = 您确定要删除该本地化语言包吗? locales.applytoall = 将更改应用于所有本地化语言包 locales.addtoother = 添加到其他本地化语言包 @@ -2554,4 +2554,3 @@ lenum.autoscale = 是否根据玩家的缩放级别缩放标记。 lenum.posi = 索引位置,用于线和四边形标记,索引零表示第一个位置。 lenum.uvi = 纹理的位置范围从零到一,用于四边形标记。 lenum.colori = 索引位置,用于线和四边形标记,索引零表示第一个颜色。 - From bfd8dbd769d13baac82921c1f8c2559ea8a65d64 Mon Sep 17 00:00:00 2001 From: GlFolker <63218676+GlennFolker@users.noreply.github.com> Date: Mon, 1 Apr 2024 21:46:44 +0700 Subject: [PATCH 063/348] Enable full customization of core launching/landing animations. (#9693) * Extract all updates and draws of core launch/land animations to CoreBlock/CoreBuild * Re-add removed methods as deprecated * Fixed tests failing * anuke said no * Extract launch effect --- core/src/mindustry/Vars.java | 5 +- core/src/mindustry/core/Control.java | 31 +--- core/src/mindustry/core/Renderer.java | 154 ++++++------------ .../mindustry/ui/dialogs/PlanetDialog.java | 9 +- .../mindustry/ui/fragments/HudFragment.java | 6 + .../world/blocks/storage/CoreBlock.java | 154 +++++++++++++++++- 6 files changed, 221 insertions(+), 138 deletions(-) diff --git a/core/src/mindustry/Vars.java b/core/src/mindustry/Vars.java index 0dc1cf9e3f..552f0ac54f 100644 --- a/core/src/mindustry/Vars.java +++ b/core/src/mindustry/Vars.java @@ -28,6 +28,7 @@ import mindustry.net.*; import mindustry.service.*; import mindustry.ui.dialogs.*; import mindustry.world.*; +import mindustry.world.blocks.storage.*; import mindustry.world.meta.*; import java.io.*; @@ -105,8 +106,8 @@ public class Vars implements Loadable{ public static final float invasionGracePeriod = 20; /** min armor fraction damage; e.g. 0.05 = at least 5% damage */ public static final float minArmorDamage = 0.1f; - /** land/launch animation duration */ - public static final float coreLandDuration = 160f; + /** @deprecated see {@link CoreBlock#landDuration} instead! */ + public static final @Deprecated float coreLandDuration = 160f; /** size of tiles in units */ public static final int tilesize = 8; /** size of one tile payload (^2) */ diff --git a/core/src/mindustry/core/Control.java b/core/src/mindustry/core/Control.java index d39b338b9b..bc730c44c4 100644 --- a/core/src/mindustry/core/Control.java +++ b/core/src/mindustry/core/Control.java @@ -30,6 +30,7 @@ import mindustry.net.*; import mindustry.type.*; import mindustry.ui.dialogs.*; import mindustry.world.*; +import mindustry.world.blocks.storage.*; import mindustry.world.blocks.storage.CoreBlock.*; import java.io.*; @@ -191,43 +192,29 @@ public class Control implements ApplicationListener, Loadable{ Events.run(Trigger.newGame, () -> { var core = player.bestCore(); - if(core == null) return; camera.position.set(core); player.set(core); float coreDelay = 0f; - if(!settings.getBool("skipcoreanimation") && !state.rules.pvp){ - coreDelay = coreLandDuration; + coreDelay = core.landDuration(); //delay player respawn so animation can play. - player.deathTimer = Player.deathDelay - coreLandDuration; + player.deathTimer = Player.deathDelay - core.landDuration(); //TODO this sounds pretty bad due to conflict if(settings.getInt("musicvol") > 0){ - Musics.land.stop(); - Musics.land.play(); - Musics.land.setVolume(settings.getInt("musicvol") / 100f); + //TODO what to do if another core with different music is already playing? + Music music = core.landMusic(); + music.stop(); + music.play(); + music.setVolume(settings.getInt("musicvol") / 100f); } - app.post(() -> ui.hudfrag.showLand()); - renderer.showLanding(); - - Time.run(coreLandDuration, () -> { - Fx.launch.at(core); - Effect.shake(5f, 5f, core); - core.thrusterTime = 1f; - - if(state.isCampaign() && Vars.showSectorLandInfo && (state.rules.sector.preset == null || state.rules.sector.preset.showSectorLandInfo)){ - ui.announce("[accent]" + state.rules.sector.name() + "\n" + - (state.rules.sector.info.resources.any() ? "[lightgray]" + bundle.get("sectors.resources") + "[white] " + - state.rules.sector.info.resources.toString(" ", u -> u.emoji()) : ""), 5); - } - }); + renderer.showLanding(core); } if(state.isCampaign()){ - //don't run when hosting, that doesn't really work. if(state.rules.sector.planet.prebuildBase){ toBePlaced.clear(); diff --git a/core/src/mindustry/core/Renderer.java b/core/src/mindustry/core/Renderer.java index 7b7d1dc155..3eb3b169b0 100644 --- a/core/src/mindustry/core/Renderer.java +++ b/core/src/mindustry/core/Renderer.java @@ -13,7 +13,6 @@ import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; import mindustry.*; -import mindustry.content.*; import mindustry.game.EventType.*; import mindustry.gen.*; import mindustry.graphics.*; @@ -30,11 +29,6 @@ public class Renderer implements ApplicationListener{ /** These are global variables, for headless access. Cached. */ public static float laserOpacity = 0.5f, bridgeOpacity = 0.75f; - private static final float cloudScaling = 1700f, cfinScl = -2f, cfinOffset = 0.3f, calphaFinOffset = 0.25f; - private static final float[] cloudAlphas = {0, 0.5f, 1f, 0.1f, 0, 0f}; - private static final float cloudAlpha = 0.81f; - private static final Interp landInterp = Interp.pow3; - public final BlockRenderer blocks = new BlockRenderer(); public final FogRenderer fog = new FogRenderer(); public final MinimapRenderer minimap = new MinimapRenderer(); @@ -55,18 +49,15 @@ public class Renderer implements ApplicationListener{ public TextureRegion[] bubbles = new TextureRegion[16], splashes = new TextureRegion[12]; public TextureRegion[][] fluidFrames; + //currently landing core, null if there are no cores or it has finished landing. private @Nullable CoreBuild landCore; private @Nullable CoreBlock launchCoreType; private Color clearColor = new Color(0f, 0f, 0f, 1f); private float - //seed for cloud visuals, 0-1 - cloudSeed = 0f, //target camera scale that is lerp-ed to targetscale = Scl.scl(4), //current actual camera scale camerascale = targetscale, - //minimum camera zoom value for landing/launching; constant TODO make larger? - minZoomScl = Scl.scl(0.02f), //starts at coreLandDuration, ends at 0. if positive, core is landing. landTime, //timer for core landing particles @@ -113,10 +104,6 @@ public class Renderer implements ApplicationListener{ setupBloom(); } - Events.run(Trigger.newGame, () -> { - landCore = player.bestCore(); - }); - EnvRenderers.init(); for(int i = 0; i < bubbles.length; i++) bubbles[i] = atlas.find("bubble-" + i); for(int i = 0; i < splashes.length; i++) splashes[i] = atlas.find("splash-" + i); @@ -181,32 +168,26 @@ public class Renderer implements ApplicationListener{ enableEffects = settings.getBool("effects"); drawDisplays = !settings.getBool("hidedisplays"); drawLight = settings.getBool("drawlight", true); - pixelate = Core.settings.getBool("pixelate"); + pixelate = settings.getBool("pixelate"); + //don't bother drawing landing animation if core is null + if(landCore == null) landTime = 0f; if(landTime > 0){ - if(!state.isPaused()){ - CoreBuild build = landCore == null ? player.bestCore() : landCore; - if(build != null){ - build.updateLandParticles(); - } - } + if(!state.isPaused()) landCore.updateLaunching(); - if(!state.isPaused()){ - landTime -= Time.delta; - } - float fin = landTime / coreLandDuration; - if(!launching) fin = 1f - fin; - camerascale = landInterp.apply(minZoomScl, Scl.scl(4f), fin); weatherAlpha = 0f; + camerascale = landCore.zoomLaunching(); - //snap camera to cutscene core regardless of player input - if(landCore != null){ - camera.position.set(landCore); - } + if(!state.isPaused()) landTime -= Time.delta; }else{ weatherAlpha = Mathf.lerpDelta(weatherAlpha, 1f, 0.08f); } + if(landCore != null && landTime <= 0f){ + landCore.endLaunch(); + landCore = null; + } + camera.width = graphics.getWidth() / camerascale; camera.height = graphics.getHeight() / camerascale; @@ -304,7 +285,7 @@ public class Renderer implements ApplicationListener{ graphics.clear(clearColor); Draw.reset(); - if(Core.settings.getBool("animatedwater") || animateShields){ + if(settings.getBool("animatedwater") || animateShields){ effectBuffer.resize(graphics.getWidth(), graphics.getHeight()); } @@ -393,7 +374,10 @@ public class Renderer implements ApplicationListener{ Draw.draw(Layer.overlayUI, overlays::drawTop); if(state.rules.fog) Draw.draw(Layer.fogOfWar, fog::drawFog); - Draw.draw(Layer.space, this::drawLanding); + Draw.draw(Layer.space, () -> { + if(landCore == null || landTime <= 0f) return; + landCore.drawLanding(launching && launchCoreType != null ? launchCoreType : (CoreBlock)landCore.block); + }); Events.fire(Trigger.drawOver); blocks.drawBlocks(); @@ -481,61 +465,6 @@ public class Renderer implements ApplicationListener{ if(state.rules.customBackgroundCallback != null && customBackgrounds.containsKey(state.rules.customBackgroundCallback)){ customBackgrounds.get(state.rules.customBackgroundCallback).run(); } - - } - - void drawLanding(){ - CoreBuild build = landCore == null ? player.bestCore() : landCore; - var clouds = assets.get("sprites/clouds.png", Texture.class); - if(landTime > 0 && build != null){ - float fout = landTime / coreLandDuration; - if(launching) fout = 1f - fout; - float fin = 1f - fout; - float scl = Scl.scl(4f) / camerascale; - float pfin = Interp.pow3Out.apply(fin), pf = Interp.pow2In.apply(fout); - - //draw particles - Draw.color(Pal.lightTrail); - Angles.randLenVectors(1, pfin, 100, 800f * scl * pfin, (ax, ay, ffin, ffout) -> { - Lines.stroke(scl * ffin * pf * 3f); - Lines.lineAngle(build.x + ax, build.y + ay, Mathf.angle(ax, ay), (ffin * 20 + 1f) * scl); - }); - Draw.color(); - - CoreBlock block = launching && launchCoreType != null ? launchCoreType : (CoreBlock)build.block; - block.drawLanding(build, build.x, build.y); - - Draw.color(); - Draw.mixcol(Color.white, Interp.pow5In.apply(fout)); - - //accent tint indicating that the core was just constructed - if(launching){ - float f = Mathf.clamp(1f - fout * 12f); - if(f > 0.001f){ - Draw.mixcol(Pal.accent, f); - } - } - - //draw clouds - if(state.rules.cloudColor.a > 0.0001f){ - float scaling = cloudScaling; - float sscl = Math.max(1f + Mathf.clamp(fin + cfinOffset)* cfinScl, 0f) * camerascale; - - Tmp.tr1.set(clouds); - Tmp.tr1.set( - (camera.position.x - camera.width/2f * sscl) / scaling, - (camera.position.y - camera.height/2f * sscl) / scaling, - (camera.position.x + camera.width/2f * sscl) / scaling, - (camera.position.y + camera.height/2f * sscl) / scaling); - - Tmp.tr1.scroll(10f * cloudSeed, 10f * cloudSeed); - - Draw.alpha(Mathf.sample(cloudAlphas, fin + calphaFinOffset) * cloudAlpha); - Draw.mixcol(state.rules.cloudColor, state.rules.cloudColor.a); - Draw.rect(Tmp.tr1, camera.position.x, camera.position.y, camera.width, camera.height); - Draw.reset(); - } - } } public void scaleCamera(float amount){ @@ -580,6 +509,13 @@ public class Renderer implements ApplicationListener{ return landTime; } + public float getLandTimeIn(){ + if(landCore == null) return 0f; + float fin = landTime / landCore.landDuration(); + if(!launching) fin = 1f - fin; + return fin; + } + public float getLandPTimer(){ return landPTimer; } @@ -588,25 +524,37 @@ public class Renderer implements ApplicationListener{ this.landPTimer = landPTimer; } + @Deprecated public void showLanding(){ - launching = false; - camerascale = minZoomScl; - landTime = coreLandDuration; - cloudSeed = Mathf.random(1f); + var core = player.bestCore(); + if(core != null) showLanding(core); } + public void showLanding(CoreBuild landCore){ + this.landCore = landCore; + launching = false; + landTime = landCore.landDuration(); + + landCore.beginLaunch(null); + camerascale = landCore.zoomLaunching(); + } + + @Deprecated public void showLaunch(CoreBlock coreType){ - Vars.ui.hudfrag.showLaunch(); - Vars.control.input.config.hideConfig(); - Vars.control.input.inv.hide(); - launchCoreType = coreType; + var core = player.team().core(); + if(core != null) showLaunch(core, coreType); + } + + public void showLaunch(CoreBuild landCore, CoreBlock coreType){ + control.input.config.hideConfig(); + control.input.inv.hide(); + + this.landCore = landCore; launching = true; - landCore = player.team().core(); - cloudSeed = Mathf.random(1f); - landTime = coreLandDuration; - if(landCore != null){ - Fx.coreLaunchConstruct.at(landCore.x, landCore.y, coreType.size); - } + landTime = landCore.landDuration(); + launchCoreType = coreType; + + landCore.beginLaunch(coreType); } public void takeMapScreenshot(){ @@ -648,7 +596,7 @@ public class Renderer implements ApplicationListener{ Fi file = screenshotDirectory.child("screenshot-" + Time.millis() + ".png"); PixmapIO.writePng(file, fullPixmap); fullPixmap.dispose(); - app.post(() -> ui.showInfoFade(Core.bundle.format("screenshot", file.toString()))); + app.post(() -> ui.showInfoFade(bundle.format("screenshot", file.toString()))); }); } diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index 9a230d0fc6..0345b04775 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -35,6 +35,7 @@ import mindustry.maps.*; import mindustry.type.*; import mindustry.ui.*; import mindustry.world.blocks.storage.*; +import mindustry.world.blocks.storage.CoreBlock.*; import static arc.Core.*; import static mindustry.Vars.*; @@ -1244,7 +1245,8 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ Events.fire(new SectorLaunchLoadoutEvent(sector, from, loadout)); - if(settings.getBool("skipcoreanimation")){ + CoreBuild core = player.team().core(); + if(core == null || settings.getBool("skipcoreanimation")){ //just... go there control.playSector(from, sector); //hide only after load screen is shown @@ -1256,9 +1258,9 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ //allow planet dialog to finish hiding before actually launching Time.runTask(5f, () -> { Runnable doLaunch = () -> { - renderer.showLaunch(schemCore); + renderer.showLaunch(core, schemCore); //run with less delay, as the loading animation is delayed by several frames - Time.runTask(coreLandDuration - 8f, () -> control.playSector(from, sector)); + Time.runTask(core.landDuration() - 8f, () -> control.playSector(from, sector)); }; //load launchFrom sector right before launching so animation is correct @@ -1276,7 +1278,6 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ }else if(mode == select){ listener.get(sector); }else if(mode == planetLaunch){ //TODO make sure it doesn't have a base already. - //TODO animation //schematic selection and cost handled by listener listener.get(sector); diff --git a/core/src/mindustry/ui/fragments/HudFragment.java b/core/src/mindustry/ui/fragments/HudFragment.java index cb92cb77ad..c882edc714 100644 --- a/core/src/mindustry/ui/fragments/HudFragment.java +++ b/core/src/mindustry/ui/fragments/HudFragment.java @@ -27,6 +27,8 @@ import mindustry.input.*; import mindustry.net.Packets.*; import mindustry.type.*; import mindustry.ui.*; +import mindustry.world.blocks.storage.*; +import mindustry.world.blocks.storage.CoreBlock.*; import static mindustry.Vars.*; import static mindustry.gen.Tex.*; @@ -584,6 +586,8 @@ public class HudFragment{ } } + /** @deprecated see {@link CoreBuild#beginLaunch(CoreBlock)} */ + @Deprecated public void showLaunch(){ float margin = 30f; @@ -602,6 +606,8 @@ public class HudFragment{ Core.scene.add(image); } + /** @deprecated see {@link CoreBuild#beginLaunch(CoreBlock)} */ + @Deprecated public void showLand(){ Image image = new Image(); image.color.a = 1f; diff --git a/core/src/mindustry/world/blocks/storage/CoreBlock.java b/core/src/mindustry/world/blocks/storage/CoreBlock.java index ff24181b79..184523e3be 100644 --- a/core/src/mindustry/world/blocks/storage/CoreBlock.java +++ b/core/src/mindustry/world/blocks/storage/CoreBlock.java @@ -1,11 +1,15 @@ package mindustry.world.blocks.storage; import arc.*; +import arc.audio.*; import arc.func.*; import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; import arc.math.geom.*; +import arc.scene.actions.*; +import arc.scene.event.*; +import arc.scene.ui.*; import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; @@ -29,6 +33,9 @@ import mindustry.world.modules.*; import static mindustry.Vars.*; public class CoreBlock extends StorageBlock{ + protected static final float cloudScaling = 1700f, cfinScl = -2f, cfinOffset = 0.3f, calphaFinOffset = 0.25f, cloudAlpha = 0.81f; + protected static final float[] cloudAlphas = {0, 0.5f, 1f, 0.1f, 0, 0f}; + //hacky way to pass item modules between methods private static ItemModule nextItems; protected static final float[] thrusterSizes = {0f, 0f, 0f, 0f, 0.3f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 0f}; @@ -42,6 +49,12 @@ public class CoreBlock extends StorageBlock{ public boolean incinerateNonBuildable = false; public UnitType unitType = UnitTypes.alpha; + public float landDuration = 160f; + public Music landMusic = Musics.land; + public Effect launchEffect = Fx.launch; + + public Interp landZoomInterp = Interp.pow3; + public float landZoomFrom = 0.02f, landZoomTo = 4f; public float captureInvicibility = 60f * 15f; @@ -217,11 +230,8 @@ public class CoreBlock extends StorageBlock{ } public void drawLanding(CoreBuild build, float x, float y){ - float fout = renderer.getLandTime() / coreLandDuration; - - if(renderer.isLaunching()) fout = 1f - fout; - - float fin = 1f - fout; + float fin = renderer.getLandTimeIn(); + float fout = 1f - fin; float scl = Scl.scl(4f) / renderer.getDisplayScale(); float shake = 0f; @@ -312,6 +322,17 @@ public class CoreBlock extends StorageBlock{ public float iframes = -1f; public float thrusterTime = 0f; + protected float cloudSeed; + + //utility methods for less Block-to-CoreBlock casts. also allows for more customization + public float landDuration(){ + return landDuration; + } + + public Music landMusic(){ + return landMusic; + } + @Override public void draw(){ //draw thrusters when just landed @@ -331,6 +352,115 @@ public class CoreBlock extends StorageBlock{ } } + // `launchType` is null if it's landing instead of launching. + public void beginLaunch(@Nullable CoreBlock launchType){ + cloudSeed = Mathf.random(1f); + if(launchType != null){ + Fx.coreLaunchConstruct.at(x, y, launchType.size); + } + + if(!headless){ + // Add fade-in and fade-out foreground when landing or launching. + if(renderer.isLaunching()){ + float margin = 30f; + + Image image = new Image(); + image.color.a = 0f; + image.touchable = Touchable.disabled; + image.setFillParent(true); + image.actions(Actions.delay((landDuration() - margin) / 60f), Actions.fadeIn(margin / 60f, Interp.pow2In), Actions.delay(6f / 60f), Actions.remove()); + image.update(() -> { + image.toFront(); + ui.loadfrag.toFront(); + if(state.isMenu()){ + image.remove(); + } + }); + Core.scene.add(image); + }else{ + Image image = new Image(); + image.color.a = 1f; + image.touchable = Touchable.disabled; + image.setFillParent(true); + image.actions(Actions.fadeOut(35f / 60f), Actions.remove()); + image.update(() -> { + image.toFront(); + ui.loadfrag.toFront(); + if(state.isMenu()){ + image.remove(); + } + }); + Core.scene.add(image); + + Time.run(landDuration(), () -> { + launchEffect.at(this); + Effect.shake(5f, 5f, this); + thrusterTime = 1f; + + if(state.isCampaign() && Vars.showSectorLandInfo && (state.rules.sector.preset == null || state.rules.sector.preset.showSectorLandInfo)){ + ui.announce("[accent]" + state.rules.sector.name() + "\n" + + (state.rules.sector.info.resources.any() ? "[lightgray]" + Core.bundle.get("sectors.resources") + "[white] " + + state.rules.sector.info.resources.toString(" ", UnlockableContent::emoji) : ""), 5); + } + }); + } + } + } + + public void endLaunch(){} + + public void drawLanding(CoreBlock block){ + var clouds = Core.assets.get("sprites/clouds.png", Texture.class); + + float fin = renderer.getLandTimeIn(); + float cameraScl = renderer.getDisplayScale(); + + float fout = 1f - fin; + float scl = Scl.scl(4f) / cameraScl; + float pfin = Interp.pow3Out.apply(fin), pf = Interp.pow2In.apply(fout); + + //draw particles + Draw.color(Pal.lightTrail); + Angles.randLenVectors(1, pfin, 100, 800f * scl * pfin, (ax, ay, ffin, ffout) -> { + Lines.stroke(scl * ffin * pf * 3f); + Lines.lineAngle(x + ax, y + ay, Mathf.angle(ax, ay), (ffin * 20 + 1f) * scl); + }); + Draw.color(); + + block.drawLanding(this, x, y); + + Draw.color(); + Draw.mixcol(Color.white, Interp.pow5In.apply(fout)); + + //accent tint indicating that the core was just constructed + if(renderer.isLaunching()){ + float f = Mathf.clamp(1f - fout * 12f); + if(f > 0.001f){ + Draw.mixcol(Pal.accent, f); + } + } + + //draw clouds + if(state.rules.cloudColor.a > 0.0001f){ + float scaling = cloudScaling; + float sscl = Math.max(1f + Mathf.clamp(fin + cfinOffset) * cfinScl, 0f) * cameraScl; + + Tmp.tr1.set(clouds); + Tmp.tr1.set( + (Core.camera.position.x - Core.camera.width/2f * sscl) / scaling, + (Core.camera.position.y - Core.camera.height/2f * sscl) / scaling, + (Core.camera.position.x + Core.camera.width/2f * sscl) / scaling, + (Core.camera.position.y + Core.camera.height/2f * sscl) / scaling); + + Tmp.tr1.scroll(10f * cloudSeed, 10f * cloudSeed); + + Draw.alpha(Mathf.sample(cloudAlphas, fin + calphaFinOffset) * cloudAlpha); + Draw.mixcol(state.rules.cloudColor, state.rules.cloudColor.a); + Draw.rect(Tmp.tr1, Core.camera.position.x, Core.camera.position.y, Core.camera.width, Core.camera.height); + Draw.reset(); + } + } + public void drawThrusters(float frame){ float length = thrusterLength * (frame - 1f) - 1f/4f; for(int i = 0; i < 4; i++){ @@ -409,9 +539,19 @@ public class CoreBlock extends StorageBlock{ thrusterTime -= Time.delta/90f; } + /** @return Camera zoom while landing or launching. May optionally do other things such as setting camera position to itself. */ + public float zoomLaunching(){ + Core.camera.position.set(this); + return landZoomInterp.apply(Scl.scl(landZoomFrom), Scl.scl(landZoomTo), renderer.getLandTimeIn()); + } + + public void updateLaunching(){ + updateLandParticles(); + } + public void updateLandParticles(){ - float time = renderer.isLaunching() ? coreLandDuration - renderer.getLandTime() : renderer.getLandTime(); - float tsize = Mathf.sample(thrusterSizes, (time + 35f) / coreLandDuration); + float in = renderer.getLandTimeIn() * landDuration(); + float tsize = Mathf.sample(thrusterSizes, (in + 35f) / landDuration()); renderer.setLandPTimer(renderer.getLandPTimer() + tsize * Time.delta); if(renderer.getLandTime() >= 1f){ From 83656a14814452897f4cc7aae85d66822344ad78 Mon Sep 17 00:00:00 2001 From: PolarStar <107398572+PoIarStar@users.noreply.github.com> Date: Mon, 1 Apr 2024 17:48:36 +0300 Subject: [PATCH 064/348] Update servers_v7.json (#9700) April fools day name change --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 56f07af980..ce7a6765fb 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -33,7 +33,7 @@ "address": ["de-free-01.hosts.optikservers.com:31528","de-prem-01.hosts.optikservers.com:35922"] }, { - "name": "CMS Empire", + "name": "Chilldustry", "address": ["trelesco.xyz", "95.84.198.97"] }, { From e177035593e0d03579ce0e6ac0930c0487a84d3f Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 2 Apr 2024 09:51:17 -0400 Subject: [PATCH 065/348] Removed unnecessary hit effect calls --- core/src/mindustry/entities/Damage.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/mindustry/entities/Damage.java b/core/src/mindustry/entities/Damage.java index 2c2426deba..158cba605b 100644 --- a/core/src/mindustry/entities/Damage.java +++ b/core/src/mindustry/entities/Damage.java @@ -293,7 +293,6 @@ public class Damage{ collided.each(c -> { if(hitter.damage > 0 && (pierceCap <= 0 || collideCount[0] < pierceCap)){ if(c.target instanceof Unit u){ - effect.at(c.x, c.y); u.collision(hitter, c.x, c.y); hitter.collision(u, c.x, c.y); collideCount[0]++; @@ -344,7 +343,6 @@ public class Damage{ Units.nearbyEnemies(team, rect.setCentered(x, y, 1f), u -> { if(u.checkTarget(hitter.type.collidesAir, hitter.type.collidesGround) && u.hittable()){ - effect.at(x, y); u.collision(hitter, x, y); hitter.collision(u, x, y); } From d3a78a9d423b6044b782e35d854592e846f8e1a1 Mon Sep 17 00:00:00 2001 From: Cubical box <67639725+BlueTheCube@users.noreply.github.com> Date: Tue, 2 Apr 2024 22:03:14 +0800 Subject: [PATCH 066/348] Fetching unit by type, build without type, and fixing a nullpointer crash (#9703) * Pluh * Pluh * Update LExecutor.java * fixed the null * Update LExecutor.java * I will point your null exception * is it null or nah * it is nah * Update LExecutor.java * Update LExecutor.java * null zero --- core/src/mindustry/logic/LExecutor.java | 14 +++++++++++--- core/src/mindustry/logic/LStatements.java | 8 +++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/core/src/mindustry/logic/LExecutor.java b/core/src/mindustry/logic/LExecutor.java index ba9b194364..91cb4575e2 100644 --- a/core/src/mindustry/logic/LExecutor.java +++ b/core/src/mindustry/logic/LExecutor.java @@ -1363,13 +1363,21 @@ public class LExecutor{ TeamData data = t.data(); switch(type){ - case unit -> exec.setobj(result, i < 0 || i >= data.units.size ? null : data.units.get(i)); + case unit -> { + UnitType type = exec.obj(extra) instanceof UnitType u ? u : null; + if(type == null){ + exec.setobj(result, i < 0 || i >= data.units.size ? null : data.units.get(i)); + }else{ + var units = data.unitCache(type); + exec.setobj(result, units == null || i < 0 || i >= units.size ? null : units.get(i)); + } + } case player -> exec.setobj(result, i < 0 || i >= data.players.size || data.players.get(i).unit().isNull() ? null : data.players.get(i).unit()); case core -> exec.setobj(result, i < 0 || i >= data.cores.size ? null : data.cores.get(i)); case build -> { Block block = exec.obj(extra) instanceof Block b ? b : null; if(block == null){ - exec.setobj(result, null); + exec.setobj(result, i < 0 || i >= data.buildings.size ? null : data.buildings.get(i)); }else{ var builds = data.getBuildings(block); exec.setobj(result, i < 0 || i >= builds.size ? null : builds.get(i)); @@ -1380,7 +1388,7 @@ public class LExecutor{ if(type == null){ exec.setnum(result, data.units.size); }else{ - exec.setnum(result, data.unitsByType[type.id].size); + exec.setnum(result, data.unitCache(type) == null ? 0 : data.unitCache(type).size); } } case coreCount -> exec.setnum(result, data.cores.size); diff --git a/core/src/mindustry/logic/LStatements.java b/core/src/mindustry/logic/LStatements.java index 29dde175b5..31cce56e0c 100644 --- a/core/src/mindustry/logic/LStatements.java +++ b/core/src/mindustry/logic/LStatements.java @@ -1856,11 +1856,17 @@ public class LStatements{ fields(table, index, i -> index = i); } - if(type == FetchType.buildCount || type == FetchType.build || type == FetchType.unitCount){ + if(type == FetchType.buildCount || type == FetchType.build){ row(table); fields(table, "block", extra, i -> extra = i); } + + if(type == FetchType.unitCount || type == FetchType.unit){ + row(table); + + fields(table, "unit", extra, i -> extra = i); + } } @Override From 0c52385c6b435855c3fe1ab2a6a8636475acd400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Mesk=C3=B3?= Date: Tue, 2 Apr 2024 16:06:23 +0200 Subject: [PATCH 067/348] Update Hungarian translation (#9694) * Update bundle_hu.properties I've translated the new strings. * Update bundle_hu.properties Requested change * Fixed capitalization --- core/assets/bundles/bundle_hu.properties | 72 ++++++++++++------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index bb618d854c..b501f41f78 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -832,7 +832,7 @@ sector.planetaryTerminal.description = A végső célpont.\n\nEzen a vízparti b sector.coastline.description = Ezen a helyen egy haditengerészeti egység technológiájának maradványait azonosították. Verd vissza az ellenséges támadásokat, foglald el ezt a szektort, és szerezd meg a technológiát. sector.navalFortress.description = Az ellenség bázist létesített egy távoli, természetes erődítményes szigeten. Pusztítsd el ezt az előőrsöt. Szerezd meg a fejlett hadihajó-technológiájukat, és fejleszd ki te is. -sector.onset.name = A Kezdet +sector.onset.name = A kezdet sector.aegis.name = Égisz sector.lake.name = Tó sector.intersect.name = Metszéspont @@ -866,7 +866,7 @@ sector.crevice.description = Ebben a szektorban az ellenség kegyetlen támadóe sector.siege.description = Ebben a szektorban két párhuzamos kanyon található, amelyek két irányból érkező támadásokat tesznek lehetővé.\nFejleszd ki a [accent]diciánt[], hogy még erősebb tankegységeket hozhass létre.\nVigyázat: ellenséges, nagy hatótávolságú rakéták észlelve. A rakéták a becsapódásuk előtt megsemmisíthetők. sector.crossroads.description = Az ellenséges támaszpontok ebben a szektorban változó terepviszonyok között alakultak ki. Ahhoz, hogy alkalmazkodni tudj, fejlessz ki különböző egységeket.\nEzenkívül egyes bázisokat pajzsok védenek. Találd ki, hogyan táplálják őket. sector.karst.description = Ez a szektor gazdag a nyersanyagokban, de amint egy új támaszpont leszáll, az ellenség megtámadja azt.\nHasználd ki a nyersanyagokat és fedezd fel a [accent]tóritkvarcot[]. -sector.origin.description = Az utolsó szektor, jelentős ellenséges jelenléttel.\nNem valószínű, hogy további fejlesztési lehetőségek maradtak – koncentrálj az ellenséges támaszpontok elpusztítására. +sector.origin.description = Az utolsó szektor, jelentős ellenséges jelenléttel.\nNem valószínű, hogy maradtak további fejlesztési lehetőségek – koncentrálj az ellenséges támaszpontok elpusztítására. status.burning.name = Égő status.freezing.name = Fagyos @@ -998,46 +998,46 @@ stat.immunities = Immunitások stat.healing = Gyógyulás ability.forcefield = Erőtér -ability.forcefield.description = Projects a force shield that absorbs bullets +ability.forcefield.description = Erőteret vetít ki, mely elnyeli a lövedékeket ability.repairfield = Javító mező -ability.repairfield.description = Repairs nearby units +ability.repairfield.description = Megjavítja a közeli egységeket ability.statusfield = Állapotmező -ability.statusfield.description = Applies a status effect to nearby units +ability.statusfield.description = Állapothatást alkalmaz a közeli egységekre ability.unitspawn = Gyár -ability.unitspawn.description = Constructs units +ability.unitspawn.description = Egységeket gyárt ability.shieldregenfield = Pajzsregeneráló mező -ability.shieldregenfield.description = Regenerates shields of nearby units +ability.shieldregenfield.description = Regenerálja a közeli egységek pajzsát ability.movelightning = Villámcsapás -ability.movelightning.description = Releases lightning while moving -ability.armorplate = Armor Plate -ability.armorplate.description = Reduces damage taken while shooting +ability.movelightning.description = Mozgás közben villámokat bocsát ki +ability.armorplate = Páncéllemez +ability.armorplate.description = Csökkenti a kapott sebzést lövés közben ability.shieldarc = Pajzs ív -ability.shieldarc.description = Projects a force shield in an arc that absorbs bullets +ability.shieldarc.description = Erőteret vetít ki egy ívben, mely elnyeli a lövedékeket ability.suppressionfield = Javítás elnyomása -ability.suppressionfield.description = Stops nearby repair buildings +ability.suppressionfield.description = Leállítja a közeli javítóépületeket ability.energyfield = Energiamező -ability.energyfield.description = Zaps nearby enemies -ability.energyfield.healdescription = Zaps nearby enemies and heals allies +ability.energyfield.description = Megrázza a közeli ellenségeket +ability.energyfield.healdescription = Megrázza a közeli ellenségeket, és gyógyítja a szövetségeseket ability.regen = Regeneráció -ability.regen.description = Regenerates own health over time -ability.liquidregen = Liquid Absorption -ability.liquidregen.description = Absorbs liquid to heal itself -ability.spawndeath = Death Spawns -ability.spawndeath.description = Releases units on death -ability.liquidexplode = Death Spillage -ability.liquidexplode.description = Spills liquid on death -ability.stat.firingrate = [stat]{0}/sec[lightgray] firing rate -ability.stat.regen = [stat]{0}[lightgray] health/sec -ability.stat.shield = [stat]{0}[lightgray] shield -ability.stat.repairspeed = [stat]{0}/sec[lightgray] repair speed -ability.stat.slurpheal = [stat]{0}[lightgray] health/liquid unit -ability.stat.cooldown = [stat]{0} sec[lightgray] cooldown -ability.stat.maxtargets = [stat]{0}[lightgray] max targets -ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] same type repair amount -ability.stat.damagereduction = [stat]{0}%[lightgray] damage reduction -ability.stat.minspeed = [stat]{0} tiles/sec[lightgray] min speed -ability.stat.duration = [stat]{0} sec[lightgray] duration -ability.stat.buildtime = [stat]{0} sec[lightgray] build time +ability.regen.description = Idővel regenerálja a saját életerejét +ability.liquidregen = Folyadékelnyelés +ability.liquidregen.description = Folyadékot nyel el, hogy gyógyítsa magát +ability.spawndeath = Szétesés +ability.spawndeath.description = Megsemmisülésekor egységeket bocsát ki +ability.liquidexplode = Szétömlés +ability.liquidexplode.description = Megsemmisülésekor folyadék ömlik ki belőle +ability.stat.firingrate = [stat]{0}/mp[lightgray] tüzelési sebesség +ability.stat.regen = [stat]{0}[lightgray] életerő/mp +ability.stat.shield = [stat]{0}[lightgray] pajzs +ability.stat.repairspeed = [stat]{0}/mp[lightgray] javítási sebesség +ability.stat.slurpheal = [stat]{0}[lightgray] életerő/folyadékegység +ability.stat.cooldown = [stat]{0} mp[lightgray] újratöltődés +ability.stat.maxtargets = [stat]{0}[lightgray] max. célpont +ability.stat.sametypehealmultiplier = [stat]{0}%[lightgray] javítási mennyiség (azonos típusnál) +ability.stat.damagereduction = [stat]{0}%[lightgray] sebzéscsökkentés +ability.stat.minspeed = [stat]{0} csempe/mp[lightgray] min. sebesség +ability.stat.duration = [stat]{0} mp[lightgray] időtartam +ability.stat.buildtime = [stat]{0} mp[lightgray] építési idő bar.onlycoredeposit = Csak a támaszpont elhelyezése megengedett bar.drilltierreq = Erősebb fúró szükséges @@ -1316,7 +1316,7 @@ rules.onlydepositcore = Csak a támaszpontok elhelyezése engedélyezett rules.derelictrepair = Az elhagyatott épületek javításának engedélyezése rules.reactorexplosions = Reaktorrobbanások rules.coreincinerates = Többletnyersanyagok megsemmisítése a támaszpontban -rules.disableworldprocessors = Világfeldolgozók letiltása +rules.disableworldprocessors = Világprocesszorok letiltása rules.schematic = Vázlatok engedélyezése rules.wavetimer = Hullámok időzítése rules.wavesending = Hullámok küldése @@ -1342,7 +1342,7 @@ rules.unitdamagemultiplier = Egység sebzésszorzója rules.unitcrashdamagemultiplier = Egység ütközési sebzésszorzója rules.solarmultiplier = Napenergia szorzója rules.unitcapvariable = A támaszpontok befolyásolják a gyártható egységek darabszámát -rules.unitpayloadsexplode = Carried Payloads Explode With The Unit +rules.unitpayloadsexplode = A szállított rakományok az egységgel együtt felrobbannak rules.unitcap = Alap egységdarabszám rules.limitarea = Játékterület korlátozása rules.enemycorebuildradius = Ellenséges támaszpont körüli tiltott zóna sugara:[lightgray] (csempe) @@ -2345,7 +2345,7 @@ lst.getblock = Csempeadatok lekérdezése tetszőleges helyen. lst.setblock = Csempeadatok beállítása tetszőleges helyen. lst.spawnunit = Egység lerakása az adott helyen. lst.applystatus = Állapothatás alkalmazása vagy törlése egy egységről. -lst.weathersense = Check if a type of weather is active. +lst.weathersense = Ellenőrzés, hogy egy bizonyos típusú időjárás aktív-e. lst.weatherset = Az időjárástípus jelenlegi állapotának megadása. lst.spawnwave = Egy hullám indítása. lst.explosion = Robbanás létrehozása az adott helyen. From 4d916f5b2394534598d3636595a2c422053f5359 Mon Sep 17 00:00:00 2001 From: Redstonneur1256 Date: Wed, 3 Apr 2024 00:52:02 +0200 Subject: [PATCH 068/348] Update servers_v7.json (#9704) --- servers_v7.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/servers_v7.json b/servers_v7.json index ce7a6765fb..58e1268346 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -269,10 +269,6 @@ "name": "Atomic", "address": ["atomic-de.ddns.net:35876", "atomic-de.ddns.net:35663", "atomic-de.ddns.net:35724", "atomic-de.ddns.net:35847"] }, - { - "name": "SkyPlex", - "address": ["mc-skyplex.net"] - }, { "name": "Gadgetroch's Server", "address": ["mindustry.gadgetroch.com", "mindustry.gadgetroch.com:6568", "mindustry.gadgetroch.com:6569"] From d1538a168dc955e984042aab0c2e84a32f4e21bc Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 4 Apr 2024 09:21:25 -0400 Subject: [PATCH 069/348] Fixed #9708 --- core/src/mindustry/world/blocks/units/UnitAssembler.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/world/blocks/units/UnitAssembler.java b/core/src/mindustry/world/blocks/units/UnitAssembler.java index bff1cdf800..f77f2119ac 100644 --- a/core/src/mindustry/world/blocks/units/UnitAssembler.java +++ b/core/src/mindustry/world/blocks/units/UnitAssembler.java @@ -23,6 +23,8 @@ import mindustry.logic.*; import mindustry.type.*; import mindustry.ui.*; import mindustry.world.*; +import mindustry.world.blocks.*; +import mindustry.world.blocks.ConstructBlock.*; import mindustry.world.blocks.payloads.*; import mindustry.world.blocks.units.UnitAssemblerModule.*; import mindustry.world.consumers.*; @@ -85,7 +87,9 @@ public class UnitAssembler extends PayloadBlock{ public boolean canPlaceOn(Tile tile, Team team, int rotation){ //overlapping construction areas not allowed; grow by a tiny amount so edges can't overlap either. Rect rect = getRect(Tmp.r1, tile.worldx() + offset, tile.worldy() + offset, rotation).grow(0.1f); - return !indexer.getFlagged(team, BlockFlag.unitAssembler).contains(b -> getRect(Tmp.r2, b.x, b.y, b.rotation).overlaps(rect)); + return + !indexer.getFlagged(team, BlockFlag.unitAssembler).contains(b -> getRect(Tmp.r2, b.x, b.y, b.rotation).overlaps(rect)) && + !team.data().getBuildings(ConstructBlock.get(size)).contains(b -> ((ConstructBuild)b).current instanceof UnitAssembler && getRect(Tmp.r2, b.x, b.y, b.rotation).overlaps(rect)); } @Override From 89433ddeb48aa3410ac316e2550798e625c113bb Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 4 Apr 2024 18:14:32 -0400 Subject: [PATCH 070/348] Trail length FPS independence --- core/src/mindustry/graphics/Trail.java | 34 ++++++++++++++++---------- gradle.properties | 2 +- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/core/src/mindustry/graphics/Trail.java b/core/src/mindustry/graphics/Trail.java index 7594257642..48e8ff900e 100644 --- a/core/src/mindustry/graphics/Trail.java +++ b/core/src/mindustry/graphics/Trail.java @@ -97,12 +97,11 @@ public class Trail{ /** Removes the last point from the trail at intervals. */ public void shorten(){ - if((counter += Time.delta) >= 1f){ - if(points.size >= 3){ - points.removeRange(0, 2); - } + int count = (int)(counter += Time.delta); + counter -= count; - counter %= 1f; + if(points.size + ((count - 1) * 3) > length * 3){ + points.removeRange(0, Math.min(3 * count - 1, points.size - 1)); } } @@ -113,18 +112,27 @@ public class Trail{ /** Adds a new point to the trail at intervals. */ public void update(float x, float y, float width){ - //TODO fix longer trails at low FPS - if((counter += Time.delta) >= 1f){ - if(points.size > length*3){ - points.removeRange(0, 2); + int count = (int)(counter += Time.delta); + counter -= count; + + if(count > 0){ + int toRemove = points.size + (count - 1 - length) * 3; + if(toRemove > 0){ + points.removeRange(0, Math.min(toRemove - 1, points.size - 1)); } - points.add(x, y, width); - - counter %= 1f; + //if lastX is -1, this trail has never updated, so only add one point - there is nothing to interpolate with + if(count == 1 || lastX == -1f){ + points.add(x, y, width); + }else{ + for(int i = 0; i < count; i++){ + float f = (i + 1f) / count; + points.add(Mathf.lerp(lastX, x, f), Mathf.lerp(lastY, y, f), Mathf.lerp(lastW, width, f)); + } + } } - //update last position regardless, so it joins + //update last position regardless, so it joins at the origin lastAngle = -Angles.angleRad(x, y, lastX, lastY); lastX = x; lastY = y; diff --git a/gradle.properties b/gradle.properties index 1733b891ba..5aca247a29 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=e312cc96f5 +archash=4ea3d3b7e8 From 275199fe1679db416532f72a80549e071f1ef728 Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 4 Apr 2024 23:24:58 -0400 Subject: [PATCH 071/348] Trail tail smoothened --- core/src/mindustry/graphics/Trail.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/mindustry/graphics/Trail.java b/core/src/mindustry/graphics/Trail.java index 48e8ff900e..813623a7bf 100644 --- a/core/src/mindustry/graphics/Trail.java +++ b/core/src/mindustry/graphics/Trail.java @@ -65,6 +65,12 @@ public class Trail{ x2 = items[i + 3]; y2 = items[i + 4]; w2 = items[i + 5]; + + if(i == 0){ + x1 = Mathf.lerp(x1, x2, counter); + y1 = Mathf.lerp(y1, y2, counter); + w1 = Mathf.lerp(w1, w2, counter); + } }else{ x2 = lastX; y2 = lastY; From ca37e1903d8aa2777ec655f37dc1ef157249a882 Mon Sep 17 00:00:00 2001 From: Hahaa13 <108379326+Hahaa13@users.noreply.github.com> Date: Fri, 5 Apr 2024 19:52:59 +0700 Subject: [PATCH 072/348] Update servers_v7.json (#9711) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 58e1268346..1bc2162697 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -279,7 +279,7 @@ }, { "name": "AZDustry", - "address": ["118.127.8.162:25628"] + "address": ["23.175.144.70:25845"] }, { "name": "Erepulo", From 7bec60de1298f9f39b3e9f85a44a55c62c830b76 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 5 Apr 2024 21:00:37 -0400 Subject: [PATCH 073/348] Fixed snow name --- core/assets/bundles/bundle.properties | 2 +- core/assets/bundles/bundle_be.properties | 2 +- core/assets/bundles/bundle_bg.properties | 2 +- core/assets/bundles/bundle_ca.properties | 2 +- core/assets/bundles/bundle_cs.properties | 2 +- core/assets/bundles/bundle_da.properties | 2 +- core/assets/bundles/bundle_de.properties | 2 +- core/assets/bundles/bundle_es.properties | 2 +- core/assets/bundles/bundle_et.properties | 2 +- core/assets/bundles/bundle_eu.properties | 2 +- core/assets/bundles/bundle_fi.properties | 2 +- core/assets/bundles/bundle_fil.properties | 2 +- core/assets/bundles/bundle_fr.properties | 2 +- core/assets/bundles/bundle_hu.properties | 2 +- core/assets/bundles/bundle_id_ID.properties | 2 +- core/assets/bundles/bundle_it.properties | 2 +- core/assets/bundles/bundle_ja.properties | 2 +- core/assets/bundles/bundle_ko.properties | 2 +- core/assets/bundles/bundle_lt.properties | 2 +- core/assets/bundles/bundle_nl.properties | 2 +- core/assets/bundles/bundle_nl_BE.properties | 2 +- core/assets/bundles/bundle_pl.properties | 2 +- core/assets/bundles/bundle_pt_BR.properties | 2 +- core/assets/bundles/bundle_pt_PT.properties | 2 +- core/assets/bundles/bundle_ro.properties | 2 +- core/assets/bundles/bundle_ru.properties | 2 +- core/assets/bundles/bundle_sr.properties | 2 +- core/assets/bundles/bundle_sv.properties | 2 +- core/assets/bundles/bundle_th.properties | 2 +- core/assets/bundles/bundle_tk.properties | 2 +- core/assets/bundles/bundle_tr.properties | 2 +- core/assets/bundles/bundle_uk_UA.properties | 2 +- core/assets/bundles/bundle_vi.properties | 2 +- core/assets/bundles/bundle_zh_CN.properties | 2 +- core/assets/bundles/bundle_zh_TW.properties | 2 +- 35 files changed, 35 insertions(+), 35 deletions(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 239a70034a..08afa6d5fd 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -738,7 +738,7 @@ error.any = Unknown network error. error.bloom = Failed to initialize bloom.\nYour device may not support it. weather.rain.name = Rain -weather.snow.name = Snow +weather.snowing.name = Snow weather.sandstorm.name = Sandstorm weather.sporestorm.name = Sporestorm weather.fog.name = Fog diff --git a/core/assets/bundles/bundle_be.properties b/core/assets/bundles/bundle_be.properties index 28be5a9d62..dea9e38c94 100644 --- a/core/assets/bundles/bundle_be.properties +++ b/core/assets/bundles/bundle_be.properties @@ -716,7 +716,7 @@ error.any = Невядомая сеткавая памылка. error.bloom = Не атрымалася ініцыялізаваць свячэнне (Bloom). \nМагчыма, зараз Вашая прылада не падтрымлівае яго. weather.rain.name = Дождж -weather.snow.name = Снег +weather.snowing.name = Снег weather.sandstorm.name = Пясчаная бура weather.sporestorm.name = Спаравая бура weather.fog.name = Туман diff --git a/core/assets/bundles/bundle_bg.properties b/core/assets/bundles/bundle_bg.properties index 7c6656a87e..a3dc513594 100644 --- a/core/assets/bundles/bundle_bg.properties +++ b/core/assets/bundles/bundle_bg.properties @@ -723,7 +723,7 @@ error.any = Неизвестна мрежова грешка. error.bloom = Неуспешно инициализиране на Сияния.\nВашето устройство може да не поддържа този ефект. weather.rain.name = Дъжд -weather.snow.name = Сняг +weather.snowing.name = Сняг weather.sandstorm.name = Пясъчна буря weather.sporestorm.name = Спорова буря weather.fog.name = Мъгла diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index 6e373a2606..8af95acac6 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -727,7 +727,7 @@ error.any = S’ha produït un error de xarxa desconegut. error.bloom = No s’ha pogut inicialitzar l’efecte «bloom».\nPotser el dispositiu no admet aquesta funció. weather.rain.name = Pluja -weather.snow.name = Neu +weather.snowing.name = Neu weather.sandstorm.name = Tempesta de sorra weather.sporestorm.name = Tempesta d’espores weather.fog.name = Boira diff --git a/core/assets/bundles/bundle_cs.properties b/core/assets/bundles/bundle_cs.properties index a44a8cea4c..5459cf49fa 100644 --- a/core/assets/bundles/bundle_cs.properties +++ b/core/assets/bundles/bundle_cs.properties @@ -725,7 +725,7 @@ error.any = Neznámá chyba sítě. error.bloom = Chyba inicializace filtru Bloom.\nTvé zařízení ho nejspíš nepodporuje. weather.rain.name = Déšť -weather.snow.name = Sníh +weather.snowing.name = Sníh weather.sandstorm.name = Písečná ouře weather.sporestorm.name = Spórová bouře weather.fog.name = Mlha diff --git a/core/assets/bundles/bundle_da.properties b/core/assets/bundles/bundle_da.properties index ba68c6cb22..563f6134f8 100644 --- a/core/assets/bundles/bundle_da.properties +++ b/core/assets/bundles/bundle_da.properties @@ -717,7 +717,7 @@ error.any = Ukendt netværksfejl. error.bloom = Kunne ikke etablere bloom-effekt.\nMåske understøtter din enhed den ikke. weather.rain.name = Regn -weather.snow.name = Sne +weather.snowing.name = Sne weather.sandstorm.name = Sandstorm weather.sporestorm.name = Sporestorm weather.fog.name = Tåge diff --git a/core/assets/bundles/bundle_de.properties b/core/assets/bundles/bundle_de.properties index ad2c09c8f7..7d1a5caa72 100644 --- a/core/assets/bundles/bundle_de.properties +++ b/core/assets/bundles/bundle_de.properties @@ -734,7 +734,7 @@ error.any = Unbekannter Netzwerkfehler. error.bloom = Bloom konnte nicht initialisiert werden.\nEs kann sein, dass dein Gerät es nicht unterstützt. weather.rain.name = Regen -weather.snow.name = Schnee +weather.snowing.name = Schnee weather.sandstorm.name = Sandsturm weather.sporestorm.name = Sporensturm weather.fog.name = Nebel diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index 37aaf8fa5c..e833d94709 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -731,7 +731,7 @@ error.any = Error de red desconocido. error.bloom = Error al cargar el efecto de bloom.\nPuede que tu dispositivo no sea compatible con esta característica. weather.rain.name = Lluvia -weather.snow.name = Nieve +weather.snowing.name = Nieve weather.sandstorm.name = Tormenta de arena weather.sporestorm.name = Tormenta de esporas weather.fog.name = Niebla diff --git a/core/assets/bundles/bundle_et.properties b/core/assets/bundles/bundle_et.properties index cf46c49acf..1f653ca8cb 100644 --- a/core/assets/bundles/bundle_et.properties +++ b/core/assets/bundles/bundle_et.properties @@ -717,7 +717,7 @@ error.any = Teadmata viga võrgus. error.bloom = Bloom-efekti lähtestamine ebaõnnestus.\nSinu seade ei pruugi seda efekti toetada. weather.rain.name = Rain -weather.snow.name = Snow +weather.snowing.name = Snow weather.sandstorm.name = Sandstorm weather.sporestorm.name = Sporestorm weather.fog.name = Fog diff --git a/core/assets/bundles/bundle_eu.properties b/core/assets/bundles/bundle_eu.properties index f97845fc3e..937455c5ae 100644 --- a/core/assets/bundles/bundle_eu.properties +++ b/core/assets/bundles/bundle_eu.properties @@ -719,7 +719,7 @@ error.any = Sareko errore ezezaguna. error.bloom = Ezin izan da distira hasieratu.\nAgian zure gailuak ez du onartzen. weather.rain.name = Rain -weather.snow.name = Snow +weather.snowing.name = Snow weather.sandstorm.name = Sandstorm weather.sporestorm.name = Sporestorm weather.fog.name = Fog diff --git a/core/assets/bundles/bundle_fi.properties b/core/assets/bundles/bundle_fi.properties index 011cb44d22..a510f47d8b 100644 --- a/core/assets/bundles/bundle_fi.properties +++ b/core/assets/bundles/bundle_fi.properties @@ -717,7 +717,7 @@ error.any = Tuntematon verkon virhe. error.bloom = Bloomin initialisointi epäonnistui.\nLaitteesi ei ehkä tue sitä. weather.rain.name = Sade -weather.snow.name = Lumi +weather.snowing.name = Lumi weather.sandstorm.name = Hiekkamyrsky weather.sporestorm.name = Sienimyräkkä weather.fog.name = Sumu diff --git a/core/assets/bundles/bundle_fil.properties b/core/assets/bundles/bundle_fil.properties index 92634c5367..4c1af29c96 100644 --- a/core/assets/bundles/bundle_fil.properties +++ b/core/assets/bundles/bundle_fil.properties @@ -717,7 +717,7 @@ error.any = Unknown network error. error.bloom = Nabigong simulan ang bloom.\nMaaaring hindi ito sinusuportahan ng iyong device. weather.rain.name = Rain -weather.snow.name = Snow +weather.snowing.name = Snow weather.sandstorm.name = Sandstorm weather.sporestorm.name = Sporestorm weather.fog.name = Fog diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index d044c45163..8b950668e4 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -737,7 +737,7 @@ error.any = Erreur de réseau inconnue. error.bloom = Échec de l'initialisation du flou lumineux.\nIl se peut que votre appareil ne le prenne pas en charge. weather.rain.name = Pluie -weather.snow.name = Neige +weather.snowing.name = Neige weather.sandstorm.name = Tempête de sable weather.sporestorm.name = Tempête de spores weather.fog.name = Brouillard diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index b501f41f78..997e33631c 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -738,7 +738,7 @@ error.any = Ismeretlen hálózati hiba. error.bloom = A bloom hatás előkészítése nem sikerült.\nElőfordulhat, hogy az eszköz nem támogatja. weather.rain.name = Eső -weather.snow.name = Hóesés +weather.snowing.name = Hóesés weather.sandstorm.name = Homokvihar weather.sporestorm.name = Spóravihar weather.fog.name = Köd diff --git a/core/assets/bundles/bundle_id_ID.properties b/core/assets/bundles/bundle_id_ID.properties index e9040f5bab..d8e91ab50c 100644 --- a/core/assets/bundles/bundle_id_ID.properties +++ b/core/assets/bundles/bundle_id_ID.properties @@ -731,7 +731,7 @@ error.any = Terjadi kesalahan Jaringan tidak diketahui. error.bloom = Gagal untuk menjalankan bloom.\nPerangkat Anda mungkin tidak mendukung fitur ini. weather.rain.name = Hujan -weather.snow.name = Salju +weather.snowing.name = Salju weather.sandstorm.name = Badai Pasir weather.sporestorm.name = Badai Spora weather.fog.name = Kabut diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties index 1db134fcd2..138b8cef13 100644 --- a/core/assets/bundles/bundle_it.properties +++ b/core/assets/bundles/bundle_it.properties @@ -721,7 +721,7 @@ error.any = Errore di rete sconosciuto. error.bloom = Errore dell'avvio delle shaders.\nIl tuo dispositivo potrebbe non supportarle. weather.rain.name = Pioggia -weather.snow.name = Neve +weather.snowing.name = Neve weather.sandstorm.name = Tempesta di Sabbia weather.sporestorm.name = Tempesta di Spore weather.fog.name = Nebbia diff --git a/core/assets/bundles/bundle_ja.properties b/core/assets/bundles/bundle_ja.properties index d67268871a..7e021ec285 100644 --- a/core/assets/bundles/bundle_ja.properties +++ b/core/assets/bundles/bundle_ja.properties @@ -725,7 +725,7 @@ error.any = 不明なネットワークエラーです。 error.bloom = ブルームの初期化に失敗しました。\n恐らくあなたのデバイスではブルームがサポートされていません。 weather.rain.name = 雨 -weather.snow.name = 雪 +weather.snowing.name = 雪 weather.sandstorm.name = 砂嵐 weather.sporestorm.name = 胞子嵐 weather.fog.name = 霧 diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index 8cdb1beacf..be380aa335 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -726,7 +726,7 @@ error.any = 알 수 없는 네트워크 오류 error.bloom = 블룸 그래픽 효과를 적용하지 못했습니다.\n기기가 이 기능을 지원하지 않는 것일 수도 있습니다. weather.rain.name = 비 -weather.snow.name = 눈 +weather.snowing.name = 눈 weather.sandstorm.name = 모래 폭풍 weather.sporestorm.name = 포자 폭풍 weather.fog.name = 안개 diff --git a/core/assets/bundles/bundle_lt.properties b/core/assets/bundles/bundle_lt.properties index 210ccafb06..68a0e9319b 100644 --- a/core/assets/bundles/bundle_lt.properties +++ b/core/assets/bundles/bundle_lt.properties @@ -717,7 +717,7 @@ error.any = Nžinoma tinklo klaida. error.bloom = Nepavyko inicijuoti spindėjimo.\nJūsų įrenginys gali nepalaikyti šios funkcijos. weather.rain.name = Rain -weather.snow.name = Snow +weather.snowing.name = Snow weather.sandstorm.name = Sandstorm weather.sporestorm.name = Sporestorm weather.fog.name = Fog diff --git a/core/assets/bundles/bundle_nl.properties b/core/assets/bundles/bundle_nl.properties index a193ffe2dd..8af4933743 100644 --- a/core/assets/bundles/bundle_nl.properties +++ b/core/assets/bundles/bundle_nl.properties @@ -728,7 +728,7 @@ error.any = Onbekende netwerk fout. error.bloom = Bloom aanzetten mislukt.\nJe apparaat ondersteunt het waarschijnlijk niet. weather.rain.name = Regen -weather.snow.name = Sneeuw +weather.snowing.name = Sneeuw weather.sandstorm.name = Zandstorm weather.sporestorm.name = Schimmelstorm weather.fog.name = Mist diff --git a/core/assets/bundles/bundle_nl_BE.properties b/core/assets/bundles/bundle_nl_BE.properties index 5691be2512..34a4da88f5 100644 --- a/core/assets/bundles/bundle_nl_BE.properties +++ b/core/assets/bundles/bundle_nl_BE.properties @@ -717,7 +717,7 @@ error.any = Unknown network error. error.bloom = Failed to initialize bloom.\nYour device may not support it. weather.rain.name = Rain -weather.snow.name = Snow +weather.snowing.name = Snow weather.sandstorm.name = Sandstorm weather.sporestorm.name = Sporestorm weather.fog.name = Fog diff --git a/core/assets/bundles/bundle_pl.properties b/core/assets/bundles/bundle_pl.properties index e991692472..9127fbfaf2 100644 --- a/core/assets/bundles/bundle_pl.properties +++ b/core/assets/bundles/bundle_pl.properties @@ -723,7 +723,7 @@ error.any = Nieznany błąd sieci. error.bloom = Nie udało się załadować funkcji bloom.\nTwoje urządzenie może nie wspierać tej funkcji. weather.rain.name = Deszcz -weather.snow.name = Śnieg +weather.snowing.name = Śnieg weather.sandstorm.name = Burza piaskowa weather.sporestorm.name = Burza zarodników weather.fog.name = Mgła diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index aea3126a21..77fd5923d3 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -731,7 +731,7 @@ error.any = Erro de rede desconhecido. error.bloom = Falha ao inicializar bloom.\nSeu dispositivo talvez não o suporte. weather.rain.name = Chuva -weather.snow.name = Neve +weather.snowing.name = Neve weather.sandstorm.name = Tempestade de Areia weather.sporestorm.name = Tempestade de Esporos weather.fog.name = Névoa diff --git a/core/assets/bundles/bundle_pt_PT.properties b/core/assets/bundles/bundle_pt_PT.properties index a0be18e995..f0d6701020 100644 --- a/core/assets/bundles/bundle_pt_PT.properties +++ b/core/assets/bundles/bundle_pt_PT.properties @@ -717,7 +717,7 @@ error.any = Erro de rede desconhecido. error.bloom = Falha ao inicializar bloom.\nSeu aparelho talvez não o suporte. weather.rain.name = Rain -weather.snow.name = Snow +weather.snowing.name = Snow weather.sandstorm.name = Sandstorm weather.sporestorm.name = Sporestorm weather.fog.name = Fog diff --git a/core/assets/bundles/bundle_ro.properties b/core/assets/bundles/bundle_ro.properties index ed8fc6bb60..409cf3be17 100644 --- a/core/assets/bundles/bundle_ro.properties +++ b/core/assets/bundles/bundle_ro.properties @@ -725,7 +725,7 @@ error.any = Eroare de rețea necunoscută. error.bloom = Inițializarea strălucirii a eșuat.\nS-ar putea ca dispozitivul tău să nu suporte funcția. weather.rain.name = Ploaie -weather.snow.name = Ninsoare +weather.snowing.name = Ninsoare weather.sandstorm.name = Furtună de nisip weather.sporestorm.name = Furtună de spori weather.fog.name = Ceață diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index 2587e514ba..13eec1407e 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -725,7 +725,7 @@ error.any = Неизвестная сетевая ошибка. error.bloom = Не удалось инициализировать свечение (Bloom).\nВозможно, ваше устройство не поддерживает его. weather.rain.name = Дождь -weather.snow.name = Снегопад +weather.snowing.name = Снегопад weather.sandstorm.name = Песчаная буря weather.sporestorm.name = Споровая буря weather.fog.name = Туман diff --git a/core/assets/bundles/bundle_sr.properties b/core/assets/bundles/bundle_sr.properties index 56582bbae8..218940ccd3 100644 --- a/core/assets/bundles/bundle_sr.properties +++ b/core/assets/bundles/bundle_sr.properties @@ -726,7 +726,7 @@ error.any = Nepoznata greška u mreži. error.bloom = Failed to initialize bloom.\nYour device may not support it. weather.rain.name = Kiša -weather.snow.name = Sneg +weather.snowing.name = Sneg weather.sandstorm.name = Peščana Oluja weather.sporestorm.name = Sporna Oluja weather.fog.name = Magla diff --git a/core/assets/bundles/bundle_sv.properties b/core/assets/bundles/bundle_sv.properties index 65d440cab3..5a30521e0c 100644 --- a/core/assets/bundles/bundle_sv.properties +++ b/core/assets/bundles/bundle_sv.properties @@ -717,7 +717,7 @@ error.any = Okänt nätverksfel. error.bloom = Failed to initialize bloom.\nYour device may not support it. weather.rain.name = Rain -weather.snow.name = Snow +weather.snowing.name = Snow weather.sandstorm.name = Sandstorm weather.sporestorm.name = Sporestorm weather.fog.name = Fog diff --git a/core/assets/bundles/bundle_th.properties b/core/assets/bundles/bundle_th.properties index 222c4fbb21..e1032d277c 100644 --- a/core/assets/bundles/bundle_th.properties +++ b/core/assets/bundles/bundle_th.properties @@ -725,7 +725,7 @@ error.any = ข้อผิดพลาด: เครือข่ายที่ error.bloom = ไม่สามารถเริ่มต้นบลูมได้\nอุปกรณ์ของคุณอาจไม่รองรับ weather.rain.name = ฝน -weather.snow.name = หิมะ +weather.snowing.name = หิมะ weather.sandstorm.name = พายุทราย weather.sporestorm.name = พายุสปอร์ weather.fog.name = หมอก diff --git a/core/assets/bundles/bundle_tk.properties b/core/assets/bundles/bundle_tk.properties index 881abd92f9..18c9cb6235 100644 --- a/core/assets/bundles/bundle_tk.properties +++ b/core/assets/bundles/bundle_tk.properties @@ -717,7 +717,7 @@ error.any = Unkown network error. error.bloom = Failed to initialize bloom.\nYour device may not support it. weather.rain.name = Rain -weather.snow.name = Snow +weather.snowing.name = Snow weather.sandstorm.name = Sandstorm weather.sporestorm.name = Sporestorm weather.fog.name = Fog diff --git a/core/assets/bundles/bundle_tr.properties b/core/assets/bundles/bundle_tr.properties index f50ff06f3a..9a60f81d7c 100644 --- a/core/assets/bundles/bundle_tr.properties +++ b/core/assets/bundles/bundle_tr.properties @@ -725,7 +725,7 @@ error.any = Bilinmeyen ağ hatası. error.bloom = Kamaşma başlatılamadı.\nCihazınız bu özelliği desteklemiyor olabilir. weather.rain.name = Yağmur -weather.snow.name = Kar +weather.snowing.name = Kar weather.sandstorm.name = Kum Fırtınası weather.sporestorm.name = Spor Fırtınası weather.fog.name = Sis diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index e6743cbee4..6b0e45de61 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -733,7 +733,7 @@ error.any = Невідома мережева помилка error.bloom = Не вдалося ініціалізувати світіння.\nВаш пристрій, мабуть, не підтримує це. weather.rain.name = Дощ -weather.snow.name = Сніг +weather.snowing.name = Сніг weather.sandstorm.name = Піщана буря weather.sporestorm.name = Спорова буря weather.fog.name = Туман diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index 377ce2fff4..59ff78b06e 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -726,7 +726,7 @@ error.any = Lỗi mạng không xác định. error.bloom = Không khởi tạo được hiệu ứng phát sáng.\nThiết bị của bạn có thể không hỗ trợ. weather.rain.name = Mưa -weather.snow.name = Tuyết +weather.snowing.name = Tuyết weather.sandstorm.name = Bão cát weather.sporestorm.name = Bão bào tử weather.fog.name = Sương mù diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index 2a74a48802..4044a20dbc 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -734,7 +734,7 @@ error.any = 未知网络错误。 error.bloom = 未能初始化光效。 \n您的设备可能不支持。 weather.rain.name = 降雨 -weather.snow.name = 降雪 +weather.snowing.name = 降雪 weather.sandstorm.name = 沙尘暴 weather.sporestorm.name = 孢子风暴 weather.fog.name = 雾 diff --git a/core/assets/bundles/bundle_zh_TW.properties b/core/assets/bundles/bundle_zh_TW.properties index 465a18589d..35cbef4819 100644 --- a/core/assets/bundles/bundle_zh_TW.properties +++ b/core/assets/bundles/bundle_zh_TW.properties @@ -731,7 +731,7 @@ error.any = 未知網路錯誤。 error.bloom = 初始化特效失敗。\n您的裝置可能不支援 weather.rain.name = 雨 -weather.snow.name = 雪 +weather.snowing.name = 雪 weather.sandstorm.name = 沙塵暴 weather.sporestorm.name = 孢子風暴 weather.fog.name = 霧 From 08c8ab08a2b8d0be42f2a35b8fb8c414f40a6f9b Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 6 Apr 2024 09:49:30 -0400 Subject: [PATCH 074/348] Fixed #9713 --- core/src/mindustry/graphics/Trail.java | 4 ++-- gradle.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/mindustry/graphics/Trail.java b/core/src/mindustry/graphics/Trail.java index 813623a7bf..426f7215b3 100644 --- a/core/src/mindustry/graphics/Trail.java +++ b/core/src/mindustry/graphics/Trail.java @@ -106,7 +106,7 @@ public class Trail{ int count = (int)(counter += Time.delta); counter -= count; - if(points.size + ((count - 1) * 3) > length * 3){ + if(points.size + ((count - 1) * 3) > length * 3 && points.size > 0){ points.removeRange(0, Math.min(3 * count - 1, points.size - 1)); } } @@ -123,7 +123,7 @@ public class Trail{ if(count > 0){ int toRemove = points.size + (count - 1 - length) * 3; - if(toRemove > 0){ + if(toRemove > 0 && points.size > 0){ points.removeRange(0, Math.min(toRemove - 1, points.size - 1)); } diff --git a/gradle.properties b/gradle.properties index 5aca247a29..ba5ca31b4a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=4ea3d3b7e8 +archash=6ad7aca01b From 8ad39a5944c787ea695977fceac03a7eae19feb0 Mon Sep 17 00:00:00 2001 From: NealRead <160118416+NealRead@users.noreply.github.com> Date: Sat, 6 Apr 2024 20:50:08 +0700 Subject: [PATCH 075/348] Update servers_v7.json (#9712) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 1bc2162697..d77ae5e3a9 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -291,7 +291,7 @@ }, { "name": "NPTS", - "address": ["94.198.54.132:26863"] + "address": ["81.94.156.54"] }, { "name": "Skywar.VN", From ef0f89efcfa42aaeb0df5ac8c9e520ab9ad79ed8 Mon Sep 17 00:00:00 2001 From: TheCrux_ <134880334+The1Crux@users.noreply.github.com> Date: Sat, 6 Apr 2024 09:50:19 -0400 Subject: [PATCH 076/348] Update servers_v7.json (#9699) don't ask how did i got 9 servers, just enjoy it --- servers_v7.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/servers_v7.json b/servers_v7.json index d77ae5e3a9..5ae3b91ff5 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -29,8 +29,8 @@ "address": ["cn1.meiqiumdt.top","cn1.meiqiumdt.top:7000","cn1.meiqiumdt.top:8000","cn1.meiqiumdt.top:7013","cn1.meiqiumdt.top:9000"] }, { - "name": "Crux's Revelations", - "address": ["de-free-01.hosts.optikservers.com:31528","de-prem-01.hosts.optikservers.com:35922"] + "name": "Crux's Citadel", + "address": ["45.158.9.198:32865","45.158.9.198:30055","45.158.9.198:32175","45.158.9.198:30252","94.130.132.149:35899","129.154.47.57:26645","94.130.132.149:35930","129.154.47.57:27993","129.154.47.57:26881"] }, { "name": "Chilldustry", From 58d6b3e2d6a646698b88a0d09490e2a0e32259dd Mon Sep 17 00:00:00 2001 From: MEEPofFaith <54301439+MEEPofFaith@users.noreply.github.com> Date: Sat, 6 Apr 2024 06:52:39 -0700 Subject: [PATCH 077/348] Make ChildComp work with turrets (#9706) --- .../mindustry/entities/comp/ChildComp.java | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/core/src/mindustry/entities/comp/ChildComp.java b/core/src/mindustry/entities/comp/ChildComp.java index f34a9198c9..03465fbf78 100644 --- a/core/src/mindustry/entities/comp/ChildComp.java +++ b/core/src/mindustry/entities/comp/ChildComp.java @@ -4,6 +4,7 @@ import arc.math.*; import arc.util.*; import mindustry.annotations.Annotations.*; import mindustry.gen.*; +import mindustry.world.blocks.defense.turrets.BaseTurret.*; @Component abstract class ChildComp implements Posc, Rotc{ @@ -18,9 +19,14 @@ abstract class ChildComp implements Posc, Rotc{ if(parent != null){ offsetX = x - parent.getX(); offsetY = y - parent.getY(); - if(rotWithParent && parent instanceof Rotc r){ - offsetPos = -r.rotation(); - offsetRot = rotation - r.rotation(); + if(rotWithParent){ + if(parent instanceof Rotc r){ + offsetPos = -r.rotation(); + offsetRot = rotation - r.rotation(); + }else if(parent instanceof BaseTurretBuild build){ + offsetPos = -build.rotation; + offsetRot = rotation - build.rotation; + } } } } @@ -28,10 +34,16 @@ abstract class ChildComp implements Posc, Rotc{ @Override public void update(){ if(parent != null){ - if(rotWithParent && parent instanceof Rotc r){ - x = parent.getX() + Angles.trnsx(r.rotation() + offsetPos, offsetX, offsetY); - y = parent.getY() + Angles.trnsy(r.rotation() + offsetPos, offsetX, offsetY); - rotation = r.rotation() + offsetRot; + if(rotWithParent){ + if(parent instanceof Rotc r){ + x = parent.getX() + Angles.trnsx(r.rotation() + offsetPos, offsetX, offsetY); + y = parent.getY() + Angles.trnsy(r.rotation() + offsetPos, offsetX, offsetY); + rotation = r.rotation() + offsetRot; + }else if(parent instanceof BaseTurretBuild build){ + x = parent.getX() + Angles.trnsx(build.rotation + offsetPos, offsetX, offsetY); + y = parent.getY() + Angles.trnsy(build.rotation + offsetPos, offsetX, offsetY); + rotation = build.rotation + offsetRot; + } }else{ x = parent.getX() + offsetX; y = parent.getY() + offsetY; From 33f01112cae7a85780c519675ec7fe41742190df Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 6 Apr 2024 18:20:45 -0400 Subject: [PATCH 078/348] Fixed #9714 --- core/src/mindustry/world/Block.java | 5 +++++ .../world/blocks/payloads/PayloadLoader.java | 2 +- .../world/blocks/payloads/PayloadUnloader.java | 2 +- .../src/mindustry/world/blocks/units/UnitFactory.java | 2 +- .../world/consumers/ConsumePowerDynamic.java | 11 ++++++++++- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/core/src/mindustry/world/Block.java b/core/src/mindustry/world/Block.java index 5dcf542400..2e9f291b7d 100644 --- a/core/src/mindustry/world/Block.java +++ b/core/src/mindustry/world/Block.java @@ -993,6 +993,11 @@ public class Block extends UnlockableContent implements Senseable{ return consume(new ConsumePowerDynamic((Floatf)usage)); } + /** Creates a consumer that consumes a dynamic amount of power. */ + public ConsumePower consumePowerDynamic(float displayed, Floatf usage){ + return consume(new ConsumePowerDynamic(displayed, (Floatf)usage)); + } + /** * Creates a consumer which stores power. * @param powerCapacity The maximum capacity in power units. diff --git a/core/src/mindustry/world/blocks/payloads/PayloadLoader.java b/core/src/mindustry/world/blocks/payloads/PayloadLoader.java index 301789e54b..d323c69feb 100644 --- a/core/src/mindustry/world/blocks/payloads/PayloadLoader.java +++ b/core/src/mindustry/world/blocks/payloads/PayloadLoader.java @@ -76,7 +76,7 @@ public class PayloadLoader extends PayloadBlock{ public void init(){ if(loadPowerDynamic){ basePowerUse = consPower != null ? consPower.usage : 0f; - consumePowerDynamic((PayloadLoaderBuild loader) -> loader.shouldConsume() ? (loader.hasBattery() && !loader.exporting ? maxPowerConsumption + basePowerUse : basePowerUse) : 0f); + consumePowerDynamic(basePowerUse, (PayloadLoaderBuild loader) -> loader.shouldConsume() ? (loader.hasBattery() && !loader.exporting ? maxPowerConsumption + basePowerUse : basePowerUse) : 0f); } super.init(); diff --git a/core/src/mindustry/world/blocks/payloads/PayloadUnloader.java b/core/src/mindustry/world/blocks/payloads/PayloadUnloader.java index fac5f083a1..205402fdd6 100644 --- a/core/src/mindustry/world/blocks/payloads/PayloadUnloader.java +++ b/core/src/mindustry/world/blocks/payloads/PayloadUnloader.java @@ -87,7 +87,7 @@ public class PayloadUnloader extends PayloadLoader{ (liquids.current() == payload.build.liquids.current() || liquids.currentAmount() <= 0.2f)){ var liq = payload.build.liquids.current(); float remaining = liquidCapacity - liquids.currentAmount(); - float flow = Math.min(Math.min(liquidsLoaded * delta(), remaining), payload.build.liquids.currentAmount()); + float flow = Math.min(Math.min(liquidsLoaded * edelta(), remaining), payload.build.liquids.currentAmount()); liquids.add(liq, flow); payload.build.liquids.remove(liq, flow); diff --git a/core/src/mindustry/world/blocks/units/UnitFactory.java b/core/src/mindustry/world/blocks/units/UnitFactory.java index 7b98b80e2f..b89d945c65 100644 --- a/core/src/mindustry/world/blocks/units/UnitFactory.java +++ b/core/src/mindustry/world/blocks/units/UnitFactory.java @@ -137,7 +137,7 @@ public class UnitFactory extends UnitBlock{ } ItemStack stack = plan.requirements[i]; - req.add(new ItemDisplay(stack.item, stack.amount, false)).pad(5); + req.add(new ItemDisplay(stack.item, stack.amount, plan.time, true)).pad(5); } }).right().grow().pad(10f); }else{ diff --git a/core/src/mindustry/world/consumers/ConsumePowerDynamic.java b/core/src/mindustry/world/consumers/ConsumePowerDynamic.java index f444c7c66f..fb40c0aaa9 100644 --- a/core/src/mindustry/world/consumers/ConsumePowerDynamic.java +++ b/core/src/mindustry/world/consumers/ConsumePowerDynamic.java @@ -7,12 +7,19 @@ import mindustry.world.meta.*; /** A power consumer that uses a dynamic amount of power. */ public class ConsumePowerDynamic extends ConsumePower{ private final Floatf usage; + private float displayedPowerUsage; public ConsumePowerDynamic(Floatf usage){ super(0, 0, false); this.usage = usage; } + public ConsumePowerDynamic(float displayed, Floatf usage){ + super(0, 0, false); + this.displayedPowerUsage = displayed; + this.usage = usage; + } + @Override public float requestedPower(Building entity){ return usage.get(entity); @@ -20,6 +27,8 @@ public class ConsumePowerDynamic extends ConsumePower{ @Override public void display(Stats stats){ - + if(displayedPowerUsage != 0f){ + stats.add(Stat.powerUse, displayedPowerUsage * 60f, StatUnit.powerSecond); + } } } From c96d4fc5006090622402f1af8f895c6dce89fae4 Mon Sep 17 00:00:00 2001 From: TheRadioactiveBanana <89061718+TheRadioactiveBanana@users.noreply.github.com> Date: Sun, 7 Apr 2024 07:45:24 +0530 Subject: [PATCH 079/348] Host massmurder (#9715) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 5ae3b91ff5..3d9e63dfc8 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -146,7 +146,7 @@ }, { "name": "Eradication Mindustry", - "address": ["140.238.246.78:7000", "140.238.246.78:7001", "140.238.246.78:7002", "140.238.246.78:7003", "130.61.22.183:7000", "130.61.22.183:7001", "130.61.22.183:7002", "130.61.22.183:7003", "130.61.22.183:7004", "130.61.22.183:7005", "130.61.220.99:7000", "130.61.220.99:7001", "130.61.220.99:7002", "130.61.220.99:7003", "130.61.220.99:7004", "130.61.220.99:7005", "62.30.47.116:7000", "62.30.47.116:7001", "62.30.47.116:7002"] + "address": ["140.238.246.78:7000", "140.238.246.78:7001", "140.238.246.78:7002", "140.238.246.78:7003", "140.238.246.78:7004", "130.61.22.183:7000", "130.61.22.183:7001", "130.61.22.183:7002", "130.61.22.183:7003", "130.61.22.183:7004", "130.61.22.183:7005", "130.61.22.183:7006", "130.61.22.183:7007", "130.61.22.183:7008", "130.61.22.183:7009"] }, { "name": "Conservatory", From a9a5fb6396bd280dec4044151b6d1f3ea1cae921 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 7 Apr 2024 12:36:45 -0400 Subject: [PATCH 080/348] Support for adding to ObjectMaps in JSON with `add: true` --- core/src/mindustry/mod/ContentParser.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/mod/ContentParser.java b/core/src/mindustry/mod/ContentParser.java index 40da31c7d1..5c2d59513f 100644 --- a/core/src/mindustry/mod/ContentParser.java +++ b/core/src/mindustry/mod/ContentParser.java @@ -1054,7 +1054,21 @@ public class ContentParser{ } Field field = metadata.field; try{ - field.set(object, parser.readValue(field.getType(), metadata.elementType, child, metadata.keyType)); + boolean mergeMap = ObjectMap.class.isAssignableFrom(field.getType()) && child.has("add") && child.get("add").isBoolean() && child.getBoolean("add", false); + + if(mergeMap){ + child.remove("add"); + } + + Object readField = parser.readValue(field.getType(), metadata.elementType, child, metadata.keyType); + + //if a map has add: true, add its contents to the map instead + if(mergeMap && field.get(object) instanceof ObjectMap baseMap){ + baseMap.putAll((ObjectMap)readField); + }else{ + field.set(object, readField); + } + }catch(IllegalAccessException ex){ throw new SerializationException("Error accessing field: " + field.getName() + " (" + type.getName() + ")", ex); }catch(SerializationException ex){ From 91fad3f7070aef9f67220f7037d108e222ebdafe Mon Sep 17 00:00:00 2001 From: TheCrux_ <134880334+The1Crux@users.noreply.github.com> Date: Sun, 7 Apr 2024 14:15:49 -0400 Subject: [PATCH 081/348] Update servers_v7.json (#9721) yes again me (toomanyservershelp) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 3d9e63dfc8..931e0738ed 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -30,7 +30,7 @@ }, { "name": "Crux's Citadel", - "address": ["45.158.9.198:32865","45.158.9.198:30055","45.158.9.198:32175","45.158.9.198:30252","94.130.132.149:35899","129.154.47.57:26645","94.130.132.149:35930","129.154.47.57:27993","129.154.47.57:26881"] + "address": ["45.158.9.198:32865","45.158.9.198:30055","45.158.9.198:32175","45.158.9.198:30252","176.9.150.40:35899","129.154.47.57:26645","176.9.150.40:35930","129.154.47.57:27993","129.154.47.57:26881","176.9.150.40:35168","176.9.150.40:35900","176.9.150.40:35031","176.9.150.40:35154"] }, { "name": "Chilldustry", From 11c1211d6534bd1633da6477127ec5a0df8b0008 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 7 Apr 2024 20:54:01 -0400 Subject: [PATCH 082/348] Fixed #9722 --- core/assets/bundles/bundle.properties | 20 +++++++++++--------- gradle.properties | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 08afa6d5fd..ef4cccb584 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -688,6 +688,7 @@ marker.shape.name = Shape marker.text.name = Text marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Background marker.outline = Outline @@ -1236,15 +1237,16 @@ keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Select Region diff --git a/gradle.properties b/gradle.properties index ba5ca31b4a..e3a8642e0a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=6ad7aca01b +archash=8b6f34c036 From 3b0fac1ba26af7ad61e4d9a988e8da83ab8b5bd3 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 8 Apr 2024 10:45:30 -0400 Subject: [PATCH 083/348] Fixed #9726 --- .../world/blocks/ConstructBlock.java | 56 +++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/core/src/mindustry/world/blocks/ConstructBlock.java b/core/src/mindustry/world/blocks/ConstructBlock.java index df7434a222..15553854a9 100644 --- a/core/src/mindustry/world/blocks/ConstructBlock.java +++ b/core/src/mindustry/world/blocks/ConstructBlock.java @@ -166,6 +166,7 @@ public class ConstructBlock extends Block{ private float[] accumulator; private float[] totalAccumulator; + private int[] itemsLeft; @Override public String getDisplayName(){ @@ -270,7 +271,7 @@ public class ConstructBlock extends Block{ for(int i = 0; i < current.requirements.length; i++){ int reqamount = Math.round(state.rules.buildCostMultiplier * current.requirements[i].amount); - accumulator[i] += Math.min(reqamount * maxProgress, reqamount - totalAccumulator[i] + 0.00001f); //add min amount progressed to the accumulator + accumulator[i] += Math.min(reqamount * maxProgress, reqamount - totalAccumulator[i]); //add min amount progressed to the accumulator totalAccumulator[i] = Math.min(totalAccumulator[i] + reqamount * maxProgress, reqamount); } @@ -279,9 +280,28 @@ public class ConstructBlock extends Block{ progress = Mathf.clamp(progress + maxProgress); if(progress >= 1f || state.rules.infiniteResources){ - if(lastBuilder == null) lastBuilder = builder; - if(!net.client()){ - constructed(tile, current, lastBuilder, (byte)rotation, builder.team, config); + boolean canFinish = true; + + //look at leftover resources to consume, get them from the core if necessary, delay building if not + if(!state.rules.infiniteResources){ + for(int i = 0; i < itemsLeft.length; i++){ + if(itemsLeft[i] > 0){ + if(core != null && core.items.has(current.requirements[i].item, itemsLeft[i])){ + core.items.remove(current.requirements[i].item, itemsLeft[i]); + itemsLeft[i] = 0; + }else{ + canFinish = false; + break; + } + } + } + } + + if(canFinish){ + if(lastBuilder == null) lastBuilder = builder; + if(!net.client()){ + constructed(tile, current, lastBuilder, (byte)rotation, builder.team, config); + } } } } @@ -321,6 +341,7 @@ public class ConstructBlock extends Block{ int accepting = Math.min(accumulated, core.storageCapacity - core.items.get(requirements[i].item)); //transfer items directly, as this is not production. core.items.add(requirements[i].item, accepting); + itemsLeft[i] -= accepting; accumulator[i] -= accepting; }else{ accumulator[i] -= accumulated; @@ -331,6 +352,14 @@ public class ConstructBlock extends Block{ progress = Mathf.clamp(progress - amount); if(progress <= current.deconstructThreshold || state.rules.infiniteResources){ + //add any leftover items that weren't obtained due to rounding errors + if(core != null){ + for(int i = 0; i < itemsLeft.length; i++){ + core.items.add(current.requirements[i].item, Mathf.clamp(itemsLeft[i], 0, core.storageCapacity - core.items.get(current.requirements[i].item))); + itemsLeft[i] = 0; + } + } + if(lastBuilder == null) lastBuilder = builder; Call.deconstructFinish(tile, this.current, lastBuilder); } @@ -341,6 +370,11 @@ public class ConstructBlock extends Block{ boolean infinite = team.rules().infiniteResources || state.rules.infiniteResources; for(int i = 0; i < current.requirements.length; i++){ + //there is no need to remove items that have already been fully taken out + if(itemsLeft[i] == 0){ + continue; + } + int sclamount = Math.round(state.rules.buildCostMultiplier * current.requirements[i].amount); int required = (int)(accumulator[i]); //calculate items that are required now @@ -360,6 +394,7 @@ public class ConstructBlock extends Block{ //remove stuff that is actually used if(remove && !infinite){ inventory.remove(current.requirements[i].item, maxUse); + itemsLeft[i] -= maxUse; } } //else, no items are required yet, so just keep going @@ -368,6 +403,7 @@ public class ConstructBlock extends Block{ return maxProgress; } + @Override public float progress(){ return progress; } @@ -380,8 +416,14 @@ public class ConstructBlock extends Block{ this.current = block; this.previous = previous; this.buildCost = block.buildCost * state.rules.buildCostMultiplier; + this.itemsLeft = new int[block.requirements.length]; this.accumulator = new float[block.requirements.length]; this.totalAccumulator = new float[block.requirements.length]; + + ItemStack[] requirements = current.requirements; + for(int i = 0; i < requirements.length; i++){ + this.itemsLeft[i] = Mathf.round(requirements[i].amount * state.rules.buildCostMultiplier); + } pathfinder.updateTile(tile); } @@ -394,8 +436,14 @@ public class ConstructBlock extends Block{ this.progress = 1f; this.current = previous; this.buildCost = previous.buildCost * state.rules.buildCostMultiplier; + this.itemsLeft = new int[previous.requirements.length]; this.accumulator = new float[previous.requirements.length]; this.totalAccumulator = new float[previous.requirements.length]; + + ItemStack[] requirements = current.requirements; + for(int i = 0; i < requirements.length; i++){ + this.itemsLeft[i] = Mathf.round(requirements[i].amount * state.rules.buildCostMultiplier * state.rules.deconstructRefundMultiplier); + } pathfinder.updateTile(tile); } From 5ee979783897470ce372b83942bc2a8dbb4cec96 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Mon, 8 Apr 2024 14:46:33 +0000 Subject: [PATCH 084/348] Automatic bundle update --- core/assets/bundles/bundle_be.properties | 20 +++++++++++--------- core/assets/bundles/bundle_bg.properties | 20 +++++++++++--------- core/assets/bundles/bundle_ca.properties | 20 +++++++++++--------- core/assets/bundles/bundle_cs.properties | 20 +++++++++++--------- core/assets/bundles/bundle_da.properties | 20 +++++++++++--------- core/assets/bundles/bundle_de.properties | 20 +++++++++++--------- core/assets/bundles/bundle_es.properties | 20 +++++++++++--------- core/assets/bundles/bundle_et.properties | 20 +++++++++++--------- core/assets/bundles/bundle_eu.properties | 20 +++++++++++--------- core/assets/bundles/bundle_fi.properties | 20 +++++++++++--------- core/assets/bundles/bundle_fil.properties | 20 +++++++++++--------- core/assets/bundles/bundle_fr.properties | 21 +++++++++++---------- core/assets/bundles/bundle_hu.properties | 21 +++++++++++---------- core/assets/bundles/bundle_id_ID.properties | 20 +++++++++++--------- core/assets/bundles/bundle_it.properties | 20 +++++++++++--------- core/assets/bundles/bundle_ja.properties | 20 +++++++++++--------- core/assets/bundles/bundle_ko.properties | 20 +++++++++++--------- core/assets/bundles/bundle_lt.properties | 20 +++++++++++--------- core/assets/bundles/bundle_nl.properties | 20 +++++++++++--------- core/assets/bundles/bundle_nl_BE.properties | 20 +++++++++++--------- core/assets/bundles/bundle_pl.properties | 20 +++++++++++--------- core/assets/bundles/bundle_pt_BR.properties | 20 +++++++++++--------- core/assets/bundles/bundle_pt_PT.properties | 20 +++++++++++--------- core/assets/bundles/bundle_ro.properties | 20 +++++++++++--------- core/assets/bundles/bundle_ru.properties | 20 +++++++++++--------- core/assets/bundles/bundle_sr.properties | 20 +++++++++++--------- core/assets/bundles/bundle_sv.properties | 20 +++++++++++--------- core/assets/bundles/bundle_th.properties | 20 +++++++++++--------- core/assets/bundles/bundle_tk.properties | 20 +++++++++++--------- core/assets/bundles/bundle_tr.properties | 20 +++++++++++--------- core/assets/bundles/bundle_uk_UA.properties | 20 +++++++++++--------- core/assets/bundles/bundle_vi.properties | 20 +++++++++++--------- core/assets/bundles/bundle_zh_CN.properties | 20 +++++++++++--------- core/assets/bundles/bundle_zh_TW.properties | 20 +++++++++++--------- 34 files changed, 374 insertions(+), 308 deletions(-) diff --git a/core/assets/bundles/bundle_be.properties b/core/assets/bundles/bundle_be.properties index dea9e38c94..2a0e4c4e9e 100644 --- a/core/assets/bundles/bundle_be.properties +++ b/core/assets/bundles/bundle_be.properties @@ -670,6 +670,7 @@ marker.shape.name = Форма marker.text.name = Тэкст marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Задні Фон marker.outline = Контур objective.research = [accent]Даследаваць:\n[]{0}[lightgray]{1} @@ -1206,15 +1207,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Перабудаваць Рэгіён keybind.schematic_select.name = Абраць Вобласць keybind.schematic_menu.name = Меню Схем diff --git a/core/assets/bundles/bundle_bg.properties b/core/assets/bundles/bundle_bg.properties index a3dc513594..530c050bbd 100644 --- a/core/assets/bundles/bundle_bg.properties +++ b/core/assets/bundles/bundle_bg.properties @@ -676,6 +676,7 @@ marker.shape.name = Shape marker.text.name = Text marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Background marker.outline = Outline objective.research = [accent]Research:\n[]{0}[lightgray]{1} @@ -1217,15 +1218,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Избери Регион keybind.schematic_menu.name = Меню със Схеми diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index 8af95acac6..f7642061f9 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -679,6 +679,7 @@ marker.shape.name = Forma marker.text.name = Text marker.line.name = Línia marker.quad.name = Rectangle +marker.texture.name = Texture marker.background = Fons marker.outline = Contorn @@ -1220,15 +1221,16 @@ keybind.unit_stance_hold_fire.name = Comportament: Mantén el foc keybind.unit_stance_pursue_target.name = Comportament: Persegueix l’objectiu keybind.unit_stance_patrol.name = Comportament: Patrulla keybind.unit_stance_ram.name = Comportament: Senzill -keybind.unit_command_move = Comportament: Mou -keybind.unit_command_repair = Comportament: Repara -keybind.unit_command_rebuild = Comportament: Reconstrueix -keybind.unit_command_assist = Comportament: Assisteix -keybind.unit_command_mine = Comportament: Extrau -keybind.unit_command_boost = Comportament: Sobrevola -keybind.unit_command_load_units = Comportament: Carrega unitats -keybind.unit_command_load_blocks = Comportament: Carrega blocs -keybind.unit_command_unload_payload = Comportament: Descarrega +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Reconstrueix la regió keybind.schematic_select.name = Selecciona una regió keybind.schematic_menu.name = Menú de plànols diff --git a/core/assets/bundles/bundle_cs.properties b/core/assets/bundles/bundle_cs.properties index 5459cf49fa..bd98459a70 100644 --- a/core/assets/bundles/bundle_cs.properties +++ b/core/assets/bundles/bundle_cs.properties @@ -678,6 +678,7 @@ marker.shape.name = Tvar marker.text.name = Text marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Pozadí marker.outline = Outline objective.research = [accent]Research:\n[]{0}[lightgray]{1} @@ -1219,15 +1220,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Přestavět Region keybind.schematic_select.name = Vybrat oblast keybind.schematic_menu.name = Nabídka šablon diff --git a/core/assets/bundles/bundle_da.properties b/core/assets/bundles/bundle_da.properties index 563f6134f8..687a4be97e 100644 --- a/core/assets/bundles/bundle_da.properties +++ b/core/assets/bundles/bundle_da.properties @@ -671,6 +671,7 @@ marker.shape.name = Shape marker.text.name = Text marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Background marker.outline = Outline objective.research = [accent]Research:\n[]{0}[lightgray]{1} @@ -1208,15 +1209,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Vælg region keybind.schematic_menu.name = Skabelon-visning diff --git a/core/assets/bundles/bundle_de.properties b/core/assets/bundles/bundle_de.properties index 7d1a5caa72..023a6da38a 100644 --- a/core/assets/bundles/bundle_de.properties +++ b/core/assets/bundles/bundle_de.properties @@ -684,6 +684,7 @@ marker.shape.name = Form marker.text.name = Text marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Hintergrund marker.outline = Umriss @@ -1230,15 +1231,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Region wiederaufbauen keybind.schematic_select.name = Bereich auswählen keybind.schematic_menu.name = Entwurfsmenü diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index e833d94709..8e2ab61238 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -681,6 +681,7 @@ marker.shape.name = Forma marker.text.name = Texto marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Fondo marker.outline = Bordes @@ -1226,15 +1227,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Reconstruir región keybind.schematic_select.name = Seleccionar región keybind.schematic_menu.name = Menú de esquemas diff --git a/core/assets/bundles/bundle_et.properties b/core/assets/bundles/bundle_et.properties index 1f653ca8cb..3acece8285 100644 --- a/core/assets/bundles/bundle_et.properties +++ b/core/assets/bundles/bundle_et.properties @@ -671,6 +671,7 @@ marker.shape.name = Shape marker.text.name = Text marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Background marker.outline = Outline objective.research = [accent]Research:\n[]{0}[lightgray]{1} @@ -1208,15 +1209,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Select Region keybind.schematic_menu.name = Schematic Menu diff --git a/core/assets/bundles/bundle_eu.properties b/core/assets/bundles/bundle_eu.properties index 937455c5ae..d29722b775 100644 --- a/core/assets/bundles/bundle_eu.properties +++ b/core/assets/bundles/bundle_eu.properties @@ -673,6 +673,7 @@ marker.shape.name = Shape marker.text.name = Text marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Background marker.outline = Outline objective.research = [accent]Research:\n[]{0}[lightgray]{1} @@ -1210,15 +1211,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Hautatu eskualdea keybind.schematic_menu.name = Eskema menua diff --git a/core/assets/bundles/bundle_fi.properties b/core/assets/bundles/bundle_fi.properties index a510f47d8b..d2cfbbeb4a 100644 --- a/core/assets/bundles/bundle_fi.properties +++ b/core/assets/bundles/bundle_fi.properties @@ -671,6 +671,7 @@ marker.shape.name = Shape marker.text.name = Teksti marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Background marker.outline = Outline objective.research = [accent]Tutki:\n[]{0}[lightgray]{1} @@ -1207,15 +1208,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Valitse alue keybind.schematic_menu.name = Kaavio Valikko diff --git a/core/assets/bundles/bundle_fil.properties b/core/assets/bundles/bundle_fil.properties index 4c1af29c96..85fe063009 100644 --- a/core/assets/bundles/bundle_fil.properties +++ b/core/assets/bundles/bundle_fil.properties @@ -671,6 +671,7 @@ marker.shape.name = Shape marker.text.name = Text marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Background marker.outline = Outline objective.research = [accent]Research:\n[]{0}[lightgray]{1} @@ -1207,15 +1208,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Select Region keybind.schematic_menu.name = Schematic Menu diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index 8b950668e4..0dea263859 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -687,6 +687,7 @@ marker.shape.name = Forme marker.text.name = Texte marker.line.name = Ligne marker.quad.name = Quad +marker.texture.name = Texture marker.background = Fond marker.outline = Contour @@ -1233,16 +1234,16 @@ keybind.unit_stance_hold_fire.name = Ordre: Ne pas tirer keybind.unit_stance_pursue_target.name = Ordre: Poursuivre la cible keybind.unit_stance_patrol.name = Ordre: Patrouille keybind.unit_stance_ram.name = Ordre: Charger - -keybind.unit_command_move = Commande: Bouger -keybind.unit_command_repair = Commande: Réparer -keybind.unit_command_rebuild = Commande: Reconstruire -keybind.unit_command_assist = Commande: Assister -keybind.unit_command_mine = Commande: Miner -keybind.unit_command_boost = Commande: Boost -keybind.unit_command_load_units = Commande: Transporter unités -keybind.unit_command_load_blocks = Commande: Transporter blocs -keybind.unit_command_unload_payload = Commande: Poser chargement +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Reconstruire la Zone keybind.schematic_select.name = Sélectionner une Région diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index 997e33631c..d3886064a3 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -688,6 +688,7 @@ marker.shape.name = Alakzat marker.text.name = Szöveg marker.line.name = Vonal marker.quad.name = Négyzet +marker.texture.name = Texture marker.background = Háttér marker.outline = Körvonal @@ -1234,16 +1235,16 @@ keybind.unit_stance_hold_fire.name = Egység viselkedése: tüzet szüntess keybind.unit_stance_pursue_target.name = Egység viselkedése: célpont követése keybind.unit_stance_patrol.name = Egység viselkedése: járőrözés keybind.unit_stance_ram.name = Egység viselkedése: ütközés - -keybind.unit_command_move = Egységparancs: mozgás -keybind.unit_command_repair = Egységparancs: javítás -keybind.unit_command_rebuild = Egységparancs: újjáépítés -keybind.unit_command_assist = Egységparancs: támogatás -keybind.unit_command_mine = Egységparancs: bányászás -keybind.unit_command_boost = Egységparancs: erősítés -keybind.unit_command_load_units = Egységparancs: egységek berakodása -keybind.unit_command_load_blocks = Egységparancs: blokkok berakodása -keybind.unit_command_unload_payload = Egységparancs: kirakodás +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Régió újjáépítése keybind.schematic_select.name = Terület kijelölése diff --git a/core/assets/bundles/bundle_id_ID.properties b/core/assets/bundles/bundle_id_ID.properties index d8e91ab50c..f9a2127af0 100644 --- a/core/assets/bundles/bundle_id_ID.properties +++ b/core/assets/bundles/bundle_id_ID.properties @@ -681,6 +681,7 @@ marker.shape.name = Bentuk marker.text.name = Teks marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Latar Belakang marker.outline = Garis Luar @@ -1226,15 +1227,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Pilih Daerah keybind.schematic_menu.name = Menu Skema diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties index 138b8cef13..c5d871dd97 100644 --- a/core/assets/bundles/bundle_it.properties +++ b/core/assets/bundles/bundle_it.properties @@ -674,6 +674,7 @@ marker.shape.name = Forma marker.text.name = Testo marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Sfondo marker.outline = Outline objective.research = [accent]Ricerca:\n[]{0}[lightgray]{1} @@ -1213,15 +1214,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Seleziona Regione keybind.schematic_menu.name = Menu Schematica diff --git a/core/assets/bundles/bundle_ja.properties b/core/assets/bundles/bundle_ja.properties index 7e021ec285..59f64d47b1 100644 --- a/core/assets/bundles/bundle_ja.properties +++ b/core/assets/bundles/bundle_ja.properties @@ -678,6 +678,7 @@ marker.shape.name = 図形 marker.text.name = 文章 marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = 背景 marker.outline = 輪郭 objective.research = [accent]Research:\n[]{0}[lightgray]{1} @@ -1219,15 +1220,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = リージョンの再構築 keybind.schematic_select.name = 範囲選択 keybind.schematic_menu.name = 設計図メニュー diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index be380aa335..f3c014faed 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -678,6 +678,7 @@ marker.shape.name = 도형 marker.text.name = 문자 marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = 배경 marker.outline = 외곽선 @@ -1218,15 +1219,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = 지역 재건 keybind.schematic_select.name = 영역 설정 keybind.schematic_menu.name = 설계도 메뉴 diff --git a/core/assets/bundles/bundle_lt.properties b/core/assets/bundles/bundle_lt.properties index 68a0e9319b..6bc26f33fd 100644 --- a/core/assets/bundles/bundle_lt.properties +++ b/core/assets/bundles/bundle_lt.properties @@ -671,6 +671,7 @@ marker.shape.name = Shape marker.text.name = Text marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Background marker.outline = Outline objective.research = [accent]Research:\n[]{0}[lightgray]{1} @@ -1208,15 +1209,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Pasirinkite Regioną keybind.schematic_menu.name = Schemų Meniu diff --git a/core/assets/bundles/bundle_nl.properties b/core/assets/bundles/bundle_nl.properties index 8af4933743..46eb33949b 100644 --- a/core/assets/bundles/bundle_nl.properties +++ b/core/assets/bundles/bundle_nl.properties @@ -682,6 +682,7 @@ marker.shape.name = Vorm marker.text.name = Tekst marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Achtergrond marker.outline = Omtrek objective.research = [accent]Onderzoek:\n[]{0}[lightgray]{1} @@ -1220,15 +1221,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Herbouw Regio keybind.schematic_select.name = Selecteer gebied keybind.schematic_menu.name = Ontwerpmenu diff --git a/core/assets/bundles/bundle_nl_BE.properties b/core/assets/bundles/bundle_nl_BE.properties index 34a4da88f5..01a6cf3f1f 100644 --- a/core/assets/bundles/bundle_nl_BE.properties +++ b/core/assets/bundles/bundle_nl_BE.properties @@ -671,6 +671,7 @@ marker.shape.name = Shape marker.text.name = Text marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Background marker.outline = Outline objective.research = [accent]Research:\n[]{0}[lightgray]{1} @@ -1208,15 +1209,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Select Region keybind.schematic_menu.name = Schematic Menu diff --git a/core/assets/bundles/bundle_pl.properties b/core/assets/bundles/bundle_pl.properties index 9127fbfaf2..0de92a7351 100644 --- a/core/assets/bundles/bundle_pl.properties +++ b/core/assets/bundles/bundle_pl.properties @@ -676,6 +676,7 @@ marker.shape.name = Figura marker.text.name = Tekst marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Tło marker.outline = Kontur objective.research = [accent]Zbadaj:\n[]{0}[lightgray]{1} @@ -1217,15 +1218,16 @@ keybind.unit_stance_hold_fire.name = Wstrzymaj ogień keybind.unit_stance_pursue_target.name = Goń Cel keybind.unit_stance_patrol.name = Patroluj keybind.unit_stance_ram.name = Taranuj -keybind.unit_command_move = Porusz -keybind.unit_command_repair = Naprawiaj -keybind.unit_command_rebuild = Odbudowywuj -keybind.unit_command_assist = Asystuj -keybind.unit_command_mine = Kop -keybind.unit_command_boost = Przyspieszaj -keybind.unit_command_load_units = Załaduj jednostki -keybind.unit_command_load_blocks = Załaduj Bloki -keybind.unit_command_unload_payload = Rozładuj Ładunek +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Odbuduj Region keybind.schematic_select.name = Wybierz Region keybind.schematic_menu.name = Menu Schematów diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index 77fd5923d3..4883362261 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -681,6 +681,7 @@ marker.shape.name = Shape marker.text.name = Texto marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Fundo marker.outline = Contorno @@ -1227,15 +1228,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Selecionar região keybind.schematic_menu.name = Menu de Esquemas diff --git a/core/assets/bundles/bundle_pt_PT.properties b/core/assets/bundles/bundle_pt_PT.properties index f0d6701020..cf29525e4c 100644 --- a/core/assets/bundles/bundle_pt_PT.properties +++ b/core/assets/bundles/bundle_pt_PT.properties @@ -671,6 +671,7 @@ marker.shape.name = Shape marker.text.name = Text marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Background marker.outline = Outline objective.research = [accent]Research:\n[]{0}[lightgray]{1} @@ -1208,15 +1209,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Selecionar região keybind.schematic_menu.name = Menu esquemático diff --git a/core/assets/bundles/bundle_ro.properties b/core/assets/bundles/bundle_ro.properties index 409cf3be17..d9e45b96ca 100644 --- a/core/assets/bundles/bundle_ro.properties +++ b/core/assets/bundles/bundle_ro.properties @@ -678,6 +678,7 @@ marker.shape.name = Shape marker.text.name = Text marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Background marker.outline = Outline objective.research = [accent]Research:\n[]{0}[lightgray]{1} @@ -1219,15 +1220,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Selectează Regiunea keybind.schematic_menu.name = Meniu Scheme diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index 13eec1407e..c5ccfc459c 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -678,6 +678,7 @@ marker.shape.name = Фигура marker.text.name = Текст marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Фон marker.outline = Контур objective.research = [accent]Исследуйте:\n[]{0}[lightgray]{1} @@ -1219,15 +1220,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Перестроить в области keybind.schematic_select.name = Выбрать область keybind.schematic_menu.name = Меню схем diff --git a/core/assets/bundles/bundle_sr.properties b/core/assets/bundles/bundle_sr.properties index 218940ccd3..52cf175915 100644 --- a/core/assets/bundles/bundle_sr.properties +++ b/core/assets/bundles/bundle_sr.properties @@ -678,6 +678,7 @@ marker.shape.name = Oblik marker.text.name = Tekst marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Pozadina marker.outline = Outline objective.research = [accent]Izuči:\n[]{0}[lightgray]{1} @@ -1221,15 +1222,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Ponovo Sagradi Region keybind.schematic_select.name = Izaberi Region keybind.schematic_menu.name = Menu Šema diff --git a/core/assets/bundles/bundle_sv.properties b/core/assets/bundles/bundle_sv.properties index 5a30521e0c..be714b7f2b 100644 --- a/core/assets/bundles/bundle_sv.properties +++ b/core/assets/bundles/bundle_sv.properties @@ -671,6 +671,7 @@ marker.shape.name = Shape marker.text.name = Text marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Background marker.outline = Outline objective.research = [accent]Research:\n[]{0}[lightgray]{1} @@ -1208,15 +1209,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Select Region keybind.schematic_menu.name = Schematic Menu diff --git a/core/assets/bundles/bundle_th.properties b/core/assets/bundles/bundle_th.properties index e1032d277c..9f5f8253b8 100644 --- a/core/assets/bundles/bundle_th.properties +++ b/core/assets/bundles/bundle_th.properties @@ -678,6 +678,7 @@ marker.shape.name = รูปทรง marker.text.name = ข้อความ marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = พื้นหลัง marker.outline = โครงร่าง objective.research = [accent]วิจัย:\n[]{0}[lightgray]{1} @@ -1220,15 +1221,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = เลือกพื้นที่สร้างใหม่ keybind.schematic_select.name = เลือกพื้นที่ keybind.schematic_menu.name = เมนูแผนผัง diff --git a/core/assets/bundles/bundle_tk.properties b/core/assets/bundles/bundle_tk.properties index 18c9cb6235..cca3894127 100644 --- a/core/assets/bundles/bundle_tk.properties +++ b/core/assets/bundles/bundle_tk.properties @@ -671,6 +671,7 @@ marker.shape.name = Shape marker.text.name = Text marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Background marker.outline = Outline objective.research = [accent]Research:\n[]{0}[lightgray]{1} @@ -1208,15 +1209,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Select Region keybind.schematic_menu.name = Schematic Menu diff --git a/core/assets/bundles/bundle_tr.properties b/core/assets/bundles/bundle_tr.properties index 9a60f81d7c..3df7fa80c7 100644 --- a/core/assets/bundles/bundle_tr.properties +++ b/core/assets/bundles/bundle_tr.properties @@ -678,6 +678,7 @@ marker.shape.name = Şekil marker.text.name = Yazı marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Arkaplan marker.outline = Anahat objective.research = [accent]Araştır:\n[]{0}[lightgray]{1} @@ -1217,15 +1218,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Alanı Geri İşaa Et keybind.schematic_select.name = Bölge Seç keybind.schematic_menu.name = Şema Menüsü diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 6b0e45de61..6f94bbdf57 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -683,6 +683,7 @@ marker.shape.name = Форма marker.text.name = Текст marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = Фон marker.outline = Контур @@ -1228,15 +1229,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Відбудувати регіон keybind.schematic_select.name = Вибрати ділянку keybind.schematic_menu.name = Меню схем diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index 59ff78b06e..931daa4dd9 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -679,6 +679,7 @@ marker.shape.name = Hình dạng marker.text.name = Văn bản marker.line.name = Đường kẻ marker.quad.name = Bốn điểm +marker.texture.name = Texture marker.background = Nền marker.outline = Đường viền objective.research = [accent]Nghiên cứu:\n[]{0}[lightgray]{1} @@ -1221,15 +1222,16 @@ keybind.unit_stance_hold_fire.name = Tư thế đơn vị: Ngừng bắn keybind.unit_stance_pursue_target.name = Tư thế đơn vị: Bám đuổi mục tiêu keybind.unit_stance_patrol.name = Tư thế đơn vị: Tuần tra keybind.unit_stance_ram.name = Tư thế đơn vị: Tông thẳng -keybind.unit_command_move = Mệnh lệnh đơn vị: Di chuyển -keybind.unit_command_repair = Mệnh lệnh đơn vị: Sửa chữa -keybind.unit_command_rebuild = Mệnh lệnh đơn vị: Xây lại -keybind.unit_command_assist = Mệnh lệnh đơn vị: Hỗ trợ -keybind.unit_command_mine = Mệnh lệnh đơn vị: Khai thác -keybind.unit_command_boost = Mệnh lệnh đơn vị: Tăng cường l -keybind.unit_command_load_units = Mệnh lệnh đơn vị: Nhập đơn vị -keybind.unit_command_load_blocks = Mệnh lệnh đơn vị: Nhập khối công trình -keybind.unit_command_unload_payload = Mệnh lệnh đơn vị: Dỡ khối hàng +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Chọn khu vực xây dựng lại keybind.schematic_select.name = Chọn khu vực keybind.schematic_menu.name = Menu bản thiết kế diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index 4044a20dbc..7386027dfa 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -684,6 +684,7 @@ marker.shape.name = 形状 marker.text.name = 文本 marker.line.name = 线 marker.quad.name = 四边形 +marker.texture.name = Texture marker.background = 背景 marker.outline = 轮廓 @@ -1230,15 +1231,16 @@ keybind.unit_stance_hold_fire.name = 单位姿态:停火 keybind.unit_stance_pursue_target.name = 单位姿态:追逐目标 keybind.unit_stance_patrol.name = 单位姿态:巡逻 keybind.unit_stance_ram.name = 单位姿态:冲锋 -keybind.unit_command_move = 单位指挥:移动 -keybind.unit_command_repair = 单位指挥:修复 -keybind.unit_command_rebuild = 单位指挥:重建 -keybind.unit_command_assist = 单位指挥:协助 -keybind.unit_command_mine = 单位指挥:采矿 -keybind.unit_command_boost = 单位指挥:助推 -keybind.unit_command_load_units = 单位指挥:拾取单位 -keybind.unit_command_load_blocks = 单位指挥:拾取建筑 -keybind.unit_command_unload_payload = 单位指挥:卸载载荷 +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = 重建建筑 keybind.schematic_select.name = 框选建筑 keybind.schematic_menu.name = 蓝图目录 diff --git a/core/assets/bundles/bundle_zh_TW.properties b/core/assets/bundles/bundle_zh_TW.properties index 35cbef4819..85afd5a50f 100644 --- a/core/assets/bundles/bundle_zh_TW.properties +++ b/core/assets/bundles/bundle_zh_TW.properties @@ -681,6 +681,7 @@ marker.shape.name = 稜框標示 marker.text.name = 文字標示 marker.line.name = Line marker.quad.name = Quad +marker.texture.name = Texture marker.background = 反黑背景 marker.outline = 描邊 @@ -1225,15 +1226,16 @@ keybind.unit_stance_hold_fire.name = Unit Stance: Hold Fire keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target keybind.unit_stance_patrol.name = Unit Stance: Patrol keybind.unit_stance_ram.name = Unit Stance: Ram -keybind.unit_command_move = Unit Command: Move -keybind.unit_command_repair = Unit Command: Repair -keybind.unit_command_rebuild = Unit Command: Rebuild -keybind.unit_command_assist = Unit Command: Assist -keybind.unit_command_mine = Unit Command: Mine -keybind.unit_command_boost = Unit Command: Boost -keybind.unit_command_load_units = Unit Command: Load Units -keybind.unit_command_load_blocks = Unit Command: Load Blocks -keybind.unit_command_unload_payload = Unit Command: Unload Payload +keybind.unit_command_move.name = Unit Command: Move +keybind.unit_command_repair.name = Unit Command: Repair +keybind.unit_command_rebuild.name = Unit Command: Rebuild +keybind.unit_command_assist.name = Unit Command: Assist +keybind.unit_command_mine.name = Unit Command: Mine +keybind.unit_command_boost.name = Unit Command: Boost +keybind.unit_command_load_units.name = Unit Command: Load Units +keybind.unit_command_load_blocks.name = Unit Command: Load Blocks +keybind.unit_command_unload_payload.name = Unit Command: Unload Payload +keybind.unit_command_enter_payload.name = Unit Command: Enter Payload keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = 選擇區域 keybind.schematic_menu.name = 藍圖目錄 From ad0d0a7e993e1242db78eba0e59b1df6373dc75c Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 8 Apr 2024 12:22:32 -0400 Subject: [PATCH 085/348] Additional construction fixes --- .../world/blocks/ConstructBlock.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/core/src/mindustry/world/blocks/ConstructBlock.java b/core/src/mindustry/world/blocks/ConstructBlock.java index 15553854a9..1b34d2d994 100644 --- a/core/src/mindustry/world/blocks/ConstructBlock.java +++ b/core/src/mindustry/world/blocks/ConstructBlock.java @@ -341,7 +341,7 @@ public class ConstructBlock extends Block{ int accepting = Math.min(accumulated, core.storageCapacity - core.items.get(requirements[i].item)); //transfer items directly, as this is not production. core.items.add(requirements[i].item, accepting); - itemsLeft[i] -= accepting; + itemsLeft[i] += accepting; accumulator[i] -= accepting; }else{ accumulator[i] -= accumulated; @@ -355,8 +355,11 @@ public class ConstructBlock extends Block{ //add any leftover items that weren't obtained due to rounding errors if(core != null){ for(int i = 0; i < itemsLeft.length; i++){ - core.items.add(current.requirements[i].item, Mathf.clamp(itemsLeft[i], 0, core.storageCapacity - core.items.get(current.requirements[i].item))); - itemsLeft[i] = 0; + int target = Mathf.round(requirements[i].amount * state.rules.buildCostMultiplier * state.rules.deconstructRefundMultiplier); + int remaining = target - itemsLeft[i]; + + core.items.add(current.requirements[i].item, Mathf.clamp(remaining, 0, core.storageCapacity - core.items.get(current.requirements[i].item))); + itemsLeft[i] = target; } } @@ -439,14 +442,14 @@ public class ConstructBlock extends Block{ this.itemsLeft = new int[previous.requirements.length]; this.accumulator = new float[previous.requirements.length]; this.totalAccumulator = new float[previous.requirements.length]; - - ItemStack[] requirements = current.requirements; - for(int i = 0; i < requirements.length; i++){ - this.itemsLeft[i] = Mathf.round(requirements[i].amount * state.rules.buildCostMultiplier * state.rules.deconstructRefundMultiplier); - } pathfinder.updateTile(tile); } + @Override + public byte version(){ + return 1; + } + @Override public void write(Writes write){ super.write(write); @@ -461,6 +464,7 @@ public class ConstructBlock extends Block{ for(int i = 0; i < accumulator.length; i++){ write.f(accumulator[i]); write.f(totalAccumulator[i]); + write.i(itemsLeft[i]); } } } @@ -479,6 +483,9 @@ public class ConstructBlock extends Block{ for(int i = 0; i < acsize; i++){ accumulator[i] = read.f(); totalAccumulator[i] = read.f(); + if(revision >= 1){ + itemsLeft[i] = read.i(); + } } } From 6b7260250af7513ecf429b7cd009f09e018e409f Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 8 Apr 2024 19:07:58 -0400 Subject: [PATCH 086/348] Fixed #9728 --- core/src/mindustry/graphics/Trail.java | 2 +- core/src/mindustry/world/blocks/ConstructBlock.java | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/src/mindustry/graphics/Trail.java b/core/src/mindustry/graphics/Trail.java index 426f7215b3..f77a31fd31 100644 --- a/core/src/mindustry/graphics/Trail.java +++ b/core/src/mindustry/graphics/Trail.java @@ -66,7 +66,7 @@ public class Trail{ y2 = items[i + 4]; w2 = items[i + 5]; - if(i == 0){ + if(i == 0 && points.size >= (length - 1) * 3){ x1 = Mathf.lerp(x1, x2, counter); y1 = Mathf.lerp(y1, y2, counter); w1 = Mathf.lerp(w1, w2, counter); diff --git a/core/src/mindustry/world/blocks/ConstructBlock.java b/core/src/mindustry/world/blocks/ConstructBlock.java index 1b34d2d994..796c7369bd 100644 --- a/core/src/mindustry/world/blocks/ConstructBlock.java +++ b/core/src/mindustry/world/blocks/ConstructBlock.java @@ -164,9 +164,9 @@ public class ConstructBlock extends Block{ public boolean wasConstructing, activeDeconstruct; public float constructColor; - private float[] accumulator; - private float[] totalAccumulator; - private int[] itemsLeft; + private @Nullable float[] accumulator; + private @Nullable float[] totalAccumulator; + private @Nullable int[] itemsLeft; @Override public String getDisplayName(){ @@ -480,6 +480,7 @@ public class ConstructBlock extends Block{ if(acsize != -1){ accumulator = new float[acsize]; totalAccumulator = new float[acsize]; + itemsLeft = new int[acsize]; for(int i = 0; i < acsize; i++){ accumulator[i] = read.f(); totalAccumulator[i] = read.f(); From e8e8189a9239bc338420c93e4f35b24be33648be Mon Sep 17 00:00:00 2001 From: Redstonneur1256 Date: Tue, 9 Apr 2024 01:51:50 +0200 Subject: [PATCH 087/348] Add ShapeMarker starting and ending angles (#9729) --- core/src/mindustry/game/MapObjectives.java | 14 ++++++++++---- core/src/mindustry/logic/LMarkerControl.java | 1 + gradle.properties | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/core/src/mindustry/game/MapObjectives.java b/core/src/mindustry/game/MapObjectives.java index fe7a3b235d..d1ba8ffbc6 100644 --- a/core/src/mindustry/game/MapObjectives.java +++ b/core/src/mindustry/game/MapObjectives.java @@ -851,7 +851,7 @@ public class MapObjectives implements Iterable, Eachable, Eachable, Eachable rotation = (float)p1; case color -> color.fromDouble(p1); case shape -> sides = (int)p1; + case arc -> startAngle = (float)p1; } } if(!Double.isNaN(p2)){ switch(type){ case shape -> fill = !Mathf.equal((float)p2, 0f); + case arc -> endAngle = (float)p2; } } diff --git a/core/src/mindustry/logic/LMarkerControl.java b/core/src/mindustry/logic/LMarkerControl.java index 16a9aaa9c6..c24775af02 100644 --- a/core/src/mindustry/logic/LMarkerControl.java +++ b/core/src/mindustry/logic/LMarkerControl.java @@ -13,6 +13,7 @@ public enum LMarkerControl{ stroke("stroke"), rotation("rotation"), shape("sides", "fill", "outline"), + arc("start", "end"), flushText("fetch"), fontSize("size"), textHeight("height"), diff --git a/gradle.properties b/gradle.properties index e3a8642e0a..25be0e37a2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=8b6f34c036 +archash=8a2decd656 From ddb8783c7d33851bf721b1aef20189319e5cbc6c Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 8 Apr 2024 20:05:03 -0400 Subject: [PATCH 088/348] White region is now 1x1 --- core/assets-raw/sprites/effects/white.png | Bin 71 -> 81 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/core/assets-raw/sprites/effects/white.png b/core/assets-raw/sprites/effects/white.png index ba9bf827c1f3fd6fc95544f0d9c34f0ffc29a2ec..e8b825eae04d68ecdc8bc6003f5880024eb09320 100644 GIT binary patch literal 81 zcmeAS@N?(olHy`uVBq!ia0y~yU|GZx^prw85kJ2JzX3_ gIA$jQ;Adgr{LTFLapzopr0B8dfHvj+t literal 71 zcmeAS@N?(olHy`uVBq!ia0y~yU|}1|LA9tC@5ms Y-L8DMZ{jL11_lNOPgg&ebxsLQ0QJBTl>h($ From 17913f8949e973717a585dcd8e082f7addf88a12 Mon Sep 17 00:00:00 2001 From: MEEPofFaith <54301439+MEEPofFaith@users.noreply.github.com> Date: Tue, 9 Apr 2024 07:52:41 -0700 Subject: [PATCH 089/348] Run applyColor before drawing unit parts (#9547) * Run applyColor before drawing unit parts * Run applyColor before drawing weapon parts --- core/src/mindustry/type/UnitType.java | 1 + core/src/mindustry/type/Weapon.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index d7dfcf16fe..b9f7ecd089 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -1270,6 +1270,7 @@ public class UnitType extends UnlockableContent implements Senseable{ DrawPart.params.life = s.fin(); } + applyColor(unit); part.draw(DrawPart.params); } } diff --git a/core/src/mindustry/type/Weapon.java b/core/src/mindustry/type/Weapon.java index 151a0f69ee..d67a1ca032 100644 --- a/core/src/mindustry/type/Weapon.java +++ b/core/src/mindustry/type/Weapon.java @@ -228,6 +228,7 @@ public class Weapon implements Cloneable{ var part = parts.get(i); DrawPart.params.setRecoil(part.recoilIndex >= 0 && mount.recoils != null ? mount.recoils[part.recoilIndex] : mount.recoil); if(part.under){ + unit.type.applyColor(unit); part.draw(DrawPart.params); } } @@ -262,6 +263,7 @@ public class Weapon implements Cloneable{ var part = parts.get(i); DrawPart.params.setRecoil(part.recoilIndex >= 0 && mount.recoils != null ? mount.recoils[part.recoilIndex] : mount.recoil); if(!part.under){ + unit.type.applyColor(unit); part.draw(DrawPart.params); } } From fd303524e9aed8759a9f683ab99df2aa872a0aea Mon Sep 17 00:00:00 2001 From: MEEPofFaith <54301439+MEEPofFaith@users.noreply.github.com> Date: Tue, 9 Apr 2024 08:16:49 -0700 Subject: [PATCH 090/348] #9706 done smarter (#9723) * Convert from explicit check to interface * Apply the interface to other turret-like blocks --- core/src/mindustry/entities/comp/ChildComp.java | 16 ++++++++-------- core/src/mindustry/world/blocks/RotBlock.java | 6 ++++++ .../world/blocks/defense/BuildTurret.java | 7 ++++++- .../world/blocks/defense/turrets/BaseTurret.java | 8 +++++++- .../world/blocks/distribution/MassDriver.java | 8 +++++++- .../world/blocks/payloads/PayloadMassDriver.java | 8 +++++++- .../world/blocks/units/RepairTurret.java | 8 +++++++- 7 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 core/src/mindustry/world/blocks/RotBlock.java diff --git a/core/src/mindustry/entities/comp/ChildComp.java b/core/src/mindustry/entities/comp/ChildComp.java index 03465fbf78..2ae553480f 100644 --- a/core/src/mindustry/entities/comp/ChildComp.java +++ b/core/src/mindustry/entities/comp/ChildComp.java @@ -4,7 +4,7 @@ import arc.math.*; import arc.util.*; import mindustry.annotations.Annotations.*; import mindustry.gen.*; -import mindustry.world.blocks.defense.turrets.BaseTurret.*; +import mindustry.world.blocks.*; @Component abstract class ChildComp implements Posc, Rotc{ @@ -23,9 +23,9 @@ abstract class ChildComp implements Posc, Rotc{ if(parent instanceof Rotc r){ offsetPos = -r.rotation(); offsetRot = rotation - r.rotation(); - }else if(parent instanceof BaseTurretBuild build){ - offsetPos = -build.rotation; - offsetRot = rotation - build.rotation; + }else if(parent instanceof RotBlock rot){ + offsetPos = -rot.buildRotation(); + offsetRot = rotation - rot.buildRotation(); } } } @@ -39,10 +39,10 @@ abstract class ChildComp implements Posc, Rotc{ x = parent.getX() + Angles.trnsx(r.rotation() + offsetPos, offsetX, offsetY); y = parent.getY() + Angles.trnsy(r.rotation() + offsetPos, offsetX, offsetY); rotation = r.rotation() + offsetRot; - }else if(parent instanceof BaseTurretBuild build){ - x = parent.getX() + Angles.trnsx(build.rotation + offsetPos, offsetX, offsetY); - y = parent.getY() + Angles.trnsy(build.rotation + offsetPos, offsetX, offsetY); - rotation = build.rotation + offsetRot; + }else if(parent instanceof RotBlock rot){ + x = parent.getX() + Angles.trnsx(rot.buildRotation() + offsetPos, offsetX, offsetY); + y = parent.getY() + Angles.trnsy(rot.buildRotation() + offsetPos, offsetX, offsetY); + rotation = rot.buildRotation() + offsetRot; } }else{ x = parent.getX() + offsetX; diff --git a/core/src/mindustry/world/blocks/RotBlock.java b/core/src/mindustry/world/blocks/RotBlock.java new file mode 100644 index 0000000000..470b9dd43a --- /dev/null +++ b/core/src/mindustry/world/blocks/RotBlock.java @@ -0,0 +1,6 @@ +package mindustry.world.blocks; + +/** Any block that has 360-degree rotation */ +public interface RotBlock{ + float buildRotation(); +} diff --git a/core/src/mindustry/world/blocks/defense/BuildTurret.java b/core/src/mindustry/world/blocks/defense/BuildTurret.java index 9dfaca633f..10330650ac 100644 --- a/core/src/mindustry/world/blocks/defense/BuildTurret.java +++ b/core/src/mindustry/world/blocks/defense/BuildTurret.java @@ -77,7 +77,7 @@ public class BuildTurret extends BaseTurret{ return new TextureRegion[]{baseRegion, region}; } - public class BuildTurretBuild extends BaseTurretBuild implements ControlBlock{ + public class BuildTurretBuild extends BaseTurretBuild implements ControlBlock, RotBlock{ public BlockUnitc unit = (BlockUnitc)unitType.create(team); public @Nullable Unit following; public @Nullable BlockPlan lastPlan; @@ -92,6 +92,11 @@ public class BuildTurret extends BaseTurret{ return true; } + @Override + public float buildRotation(){ + return unit.rotation(); + } + @Override public Unit unit(){ //make sure stats are correct diff --git a/core/src/mindustry/world/blocks/defense/turrets/BaseTurret.java b/core/src/mindustry/world/blocks/defense/turrets/BaseTurret.java index a959c00ba2..dbe7794611 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/BaseTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/BaseTurret.java @@ -9,6 +9,7 @@ import mindustry.gen.*; import mindustry.graphics.*; import mindustry.logic.*; import mindustry.world.*; +import mindustry.world.blocks.*; import mindustry.world.consumers.*; import mindustry.world.meta.*; @@ -79,7 +80,7 @@ public class BaseTurret extends Block{ stats.add(Stat.shootRange, range / tilesize, StatUnit.blocks); } - public class BaseTurretBuild extends Building implements Ranged{ + public class BaseTurretBuild extends Building implements Ranged, RotBlock{ public float rotation = 90; @Override @@ -87,6 +88,11 @@ public class BaseTurret extends Block{ return range; } + @Override + public float buildRotation(){ + return rotation; + } + @Override public void drawSelect(){ Drawf.dashCircle(x, y, range(), team.color); diff --git a/core/src/mindustry/world/blocks/distribution/MassDriver.java b/core/src/mindustry/world/blocks/distribution/MassDriver.java index 6366419242..7fc0d5f0ec 100644 --- a/core/src/mindustry/world/blocks/distribution/MassDriver.java +++ b/core/src/mindustry/world/blocks/distribution/MassDriver.java @@ -18,6 +18,7 @@ import mindustry.graphics.*; import mindustry.logic.*; import mindustry.type.*; import mindustry.world.*; +import mindustry.world.blocks.*; import mindustry.world.meta.*; import static mindustry.Vars.*; @@ -104,7 +105,7 @@ public class MassDriver extends Block{ } } - public class MassDriverBuild extends Building{ + public class MassDriverBuild extends Building implements RotBlock{ public int link = -1; public float rotation = 90; public float reloadCounter = 0f; @@ -112,6 +113,11 @@ public class MassDriver extends Block{ //TODO use queue? this array usually holds about 3 shooters max anyway public OrderedSet waitingShooters = new OrderedSet<>(); + @Override + public float buildRotation(){ + return rotation; + } + public Building currentShooter(){ return waitingShooters.isEmpty() ? null : waitingShooters.first(); } diff --git a/core/src/mindustry/world/blocks/payloads/PayloadMassDriver.java b/core/src/mindustry/world/blocks/payloads/PayloadMassDriver.java index 314e77d5f7..0b3efc09c9 100644 --- a/core/src/mindustry/world/blocks/payloads/PayloadMassDriver.java +++ b/core/src/mindustry/world/blocks/payloads/PayloadMassDriver.java @@ -14,6 +14,7 @@ import mindustry.entities.units.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.logic.*; +import mindustry.world.blocks.*; import mindustry.world.meta.*; import static mindustry.Vars.*; @@ -126,7 +127,7 @@ public class PayloadMassDriver extends PayloadBlock{ return new TextureRegion[]{leftRegion, rightRegion, capRegion}; } - public class PayloadDriverBuild extends PayloadBlockBuild{ + public class PayloadDriverBuild extends PayloadBlockBuild implements RotBlock{ public int link = -1; public float turretRotation = 90; public float reloadCounter = 0f, charge = 0f; @@ -143,6 +144,11 @@ public class PayloadMassDriver extends PayloadBlock{ return waitingShooters.isEmpty() ? null : waitingShooters.first(); } + @Override + public float buildRotation(){ + return rotation; + } + @Override public void updateTile(){ super.updateTile(); diff --git a/core/src/mindustry/world/blocks/units/RepairTurret.java b/core/src/mindustry/world/blocks/units/RepairTurret.java index 87624efb84..86b29c79a4 100644 --- a/core/src/mindustry/world/blocks/units/RepairTurret.java +++ b/core/src/mindustry/world/blocks/units/RepairTurret.java @@ -16,6 +16,7 @@ import mindustry.gen.*; import mindustry.graphics.*; import mindustry.logic.*; import mindustry.world.*; +import mindustry.world.blocks.*; import mindustry.world.consumers.*; import mindustry.world.meta.*; @@ -147,11 +148,16 @@ public class RepairTurret extends Block{ } } - public class RepairPointBuild extends Building implements Ranged{ + public class RepairPointBuild extends Building implements Ranged, RotBlock{ public Unit target; public Vec2 offset = new Vec2(), lastEnd = new Vec2(); public float strength, rotation = 90; + @Override + public float buildRotation(){ + return rotation; + } + @Override public void draw(){ Draw.rect(baseRegion, x, y); From e280edac67d0b5e36a027a2dd61a123e14ec5b89 Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 10 Apr 2024 15:31:09 -0400 Subject: [PATCH 091/348] Fixed #9731 --- core/src/mindustry/content/Blocks.java | 3 +++ core/src/mindustry/entities/Puddles.java | 2 +- core/src/mindustry/entities/comp/PuddleComp.java | 1 + core/src/mindustry/ui/fragments/HintsFragment.java | 4 +++- .../world/blocks/distribution/StackConveyor.java | 7 +++++++ .../world/blocks/payloads/PayloadLoader.java | 12 +++++++++++- .../mindustry/world/blocks/sandbox/LiquidVoid.java | 6 ++++++ 7 files changed, 32 insertions(+), 3 deletions(-) diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 08eb582a78..2e3d58b840 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -2782,6 +2782,7 @@ public class Blocks{ ambientSoundVolume = 0.06f; hasLiquids = true; boostScale = 1f / 9f; + itemCapacity = 0; outputLiquid = new LiquidStack(Liquids.water, 30f / 60f); consumePower(0.5f); liquidCapacity = 60f; @@ -5791,6 +5792,8 @@ public class Blocks{ heatOutput = 1000f; warmupRate = 1000f; regionRotated1 = 1; + itemCapacity = 0; + alwaysUnlocked = false; ambientSound = Sounds.none; }}; diff --git a/core/src/mindustry/entities/Puddles.java b/core/src/mindustry/entities/Puddles.java index ec546882b4..7c3a555154 100644 --- a/core/src/mindustry/entities/Puddles.java +++ b/core/src/mindustry/entities/Puddles.java @@ -74,7 +74,7 @@ public class Puddles{ Puddle puddle = Puddle.create(); puddle.tile = tile; puddle.liquid = liquid; - puddle.amount = amount; + puddle.amount = Math.min(amount, maxLiquid); puddle.set(ax, ay); register(puddle); puddle.add(); diff --git a/core/src/mindustry/entities/comp/PuddleComp.java b/core/src/mindustry/entities/comp/PuddleComp.java index 090a881060..a46bf01c32 100644 --- a/core/src/mindustry/entities/comp/PuddleComp.java +++ b/core/src/mindustry/entities/comp/PuddleComp.java @@ -59,6 +59,7 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc, Syncc{ amount -= Time.delta * (1f - liquid.viscosity) / (5f + addSpeed); amount += accepting; + amount = Math.min(amount, maxLiquid); accepting = 0f; if(amount >= maxLiquid / 1.5f){ diff --git a/core/src/mindustry/ui/fragments/HintsFragment.java b/core/src/mindustry/ui/fragments/HintsFragment.java index 2595fbb041..75528b9923 100644 --- a/core/src/mindustry/ui/fragments/HintsFragment.java +++ b/core/src/mindustry/ui/fragments/HintsFragment.java @@ -11,6 +11,7 @@ import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; import mindustry.*; +import mindustry.ai.types.*; import mindustry.content.*; import mindustry.game.EventType.*; import mindustry.game.*; @@ -169,7 +170,8 @@ public class HintsFragment{ depositItems(() -> player.unit().hasItem(), () -> !player.unit().hasItem()), desktopPause(visibleDesktop, () -> isTutorial.get() && !Vars.net.active() && state.wave >= 2, () -> Core.input.keyTap(Binding.pause)), unitControl(() -> isSerpulo() && state.rules.defaultTeam.data().units.size > 2 && !net.active() && !player.dead(), () -> !player.dead() && !player.unit().spawnedByCore), - unitSelectControl(() -> isSerpulo() && state.rules.defaultTeam.data().units.size > 3 && !net.active() && !player.dead(), () -> control.input.commandMode && control.input.selectedUnits.size > 0), + unitSelectControl(() -> isSerpulo() && state.rules.defaultTeam.data().units.size > 3 && !net.active() && !player.dead(), + () -> control.input.commandMode && control.input.selectedUnits.size > 0 && control.input.selectedUnits.first().controller() instanceof CommandAI ai && ai.targetPos != null), respawn(visibleMobile, () -> !player.dead() && !player.unit().spawnedByCore, () -> !player.dead() && player.unit().spawnedByCore), launch(() -> (isTutorial.get() || Vars.state.rules.sector == SectorPresets.onset.sector) && state.rules.sector.isCaptured(), () -> ui.planet.isShown()), schematicSelect(visibleDesktop, () -> ui.hints.placedBlocks.contains(Blocks.router) || ui.hints.placedBlocks.contains(Blocks.ductRouter), () -> Core.input.keyRelease(Binding.schematic_select) || Core.input.keyTap(Binding.pick)), diff --git a/core/src/mindustry/world/blocks/distribution/StackConveyor.java b/core/src/mindustry/world/blocks/distribution/StackConveyor.java index e107b08a67..357c3f8705 100644 --- a/core/src/mindustry/world/blocks/distribution/StackConveyor.java +++ b/core/src/mindustry/world/blocks/distribution/StackConveyor.java @@ -189,6 +189,13 @@ public class StackConveyor extends Block implements Autotiler{ Draw.rect(lastItem.fullIcon, Tmp.v1.x, Tmp.v1.y, size, size, 0); } + @Override + public void dropped(){ + super.dropped(); + var prev = Geometry.d4[(rotation + 2) % 4]; + link = Point2.pack(tile.x + prev.x, tile.y + prev.y); + } + @Override public void drawCracks(){ Draw.z(Layer.block - 0.15f); diff --git a/core/src/mindustry/world/blocks/payloads/PayloadLoader.java b/core/src/mindustry/world/blocks/payloads/PayloadLoader.java index d323c69feb..67a01ba0a7 100644 --- a/core/src/mindustry/world/blocks/payloads/PayloadLoader.java +++ b/core/src/mindustry/world/blocks/payloads/PayloadLoader.java @@ -12,6 +12,7 @@ import mindustry.graphics.*; import mindustry.type.*; import mindustry.ui.*; import mindustry.world.blocks.payloads.PayloadUnloader.*; +import mindustry.world.blocks.sandbox.*; import static mindustry.Vars.*; @@ -152,6 +153,7 @@ public class PayloadLoader extends PayloadBlock{ //load up items if(payload.block().hasItems && items.any()){ + boolean acceptedAny = false; if(efficiency > 0.01f && timer(timerLoad, loadTime / efficiency)){ //load up items a set amount of times for(int j = 0; j < itemsLoaded && items.any(); j++){ @@ -162,6 +164,7 @@ public class PayloadLoader extends PayloadBlock{ if(payload.build.acceptItem(payload.build, item)){ payload.build.handleItem(payload.build, item); items.remove(item, 1); + acceptedAny = true; break; }else if(payload.block().separateItemCapacity || payload.block().consumesItem(item)){ exporting = true; @@ -171,6 +174,9 @@ public class PayloadLoader extends PayloadBlock{ } } } + if(!acceptedAny){ + exporting = true; + } } //load up liquids @@ -180,8 +186,12 @@ public class PayloadLoader extends PayloadBlock{ float flow = Math.min(Math.min(liquidsLoaded * edelta(), payload.block().liquidCapacity - payload.build.liquids.get(liq)), total); //TODO potential crash here if(payload.build.acceptLiquid(payload.build, liq)){ - payload.build.liquids.add(liq, flow); + if(!(payload.block() instanceof LiquidVoid)){ + payload.build.liquids.add(liq, flow); + } liquids.remove(liq, flow); + }else{ + exporting = true; } } diff --git a/core/src/mindustry/world/blocks/sandbox/LiquidVoid.java b/core/src/mindustry/world/blocks/sandbox/LiquidVoid.java index ef2c62c341..bc6a56cfe8 100644 --- a/core/src/mindustry/world/blocks/sandbox/LiquidVoid.java +++ b/core/src/mindustry/world/blocks/sandbox/LiquidVoid.java @@ -24,6 +24,12 @@ public class LiquidVoid extends Block{ } public class LiquidVoidBuild extends Building{ + @Override + public void placed(){ + super.placed(); + liquids.clear(); + } + @Override public boolean acceptLiquid(Building source, Liquid liquid){ return enabled; From 43fba9b6478c7b020b7a54e01cf00990bdf006e2 Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 10 Apr 2024 16:26:08 -0400 Subject: [PATCH 092/348] typo --- core/src/mindustry/content/Blocks.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 2e3d58b840..0cda3f8d2c 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -5793,7 +5793,7 @@ public class Blocks{ warmupRate = 1000f; regionRotated1 = 1; itemCapacity = 0; - alwaysUnlocked = false; + alwaysUnlocked = true; ambientSound = Sounds.none; }}; From 24202dd20f21c12441f098681ed8befa7a02da26 Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 10 Apr 2024 19:24:28 -0400 Subject: [PATCH 093/348] Fixed #9732 --- core/src/mindustry/content/Blocks.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 0cda3f8d2c..e70394c328 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -1181,6 +1181,7 @@ public class Blocks{ rotate = true; invertFlip = true; group = BlockGroup.liquids; + itemCapacity = 0; liquidCapacity = 50f; @@ -1231,6 +1232,7 @@ public class Blocks{ }}); researchCostMultiplier = 1.1f; + itemCapacity = 0; liquidCapacity = 40f; consumePower(2f); ambientSound = Sounds.extractLoop; From a8135aa042885ebbd415cd21a88bf52f1244d329 Mon Sep 17 00:00:00 2001 From: ApsZoldat <128713348+ApsZoldat@users.noreply.github.com> Date: Fri, 12 Apr 2024 00:56:34 +0300 Subject: [PATCH 094/348] Custom rules dialog enhancement, small fixes (#9665) * Rule categories and search in custom rules dialog * Fix wrong info about format placeholders in all unfinished locales * Rule search fix, rule info system, some stuff for easier rules dialog modding * Why not to fix what i can? --- core/assets/bundles/bundle.properties | 3 + core/assets/bundles/bundle_be.properties | 4 +- core/assets/bundles/bundle_bg.properties | 4 +- core/assets/bundles/bundle_ca.properties | 4 +- core/assets/bundles/bundle_cs.properties | 4 +- core/assets/bundles/bundle_da.properties | 4 +- core/assets/bundles/bundle_de.properties | 4 +- core/assets/bundles/bundle_es.properties | 4 +- core/assets/bundles/bundle_et.properties | 4 +- core/assets/bundles/bundle_eu.properties | 4 +- core/assets/bundles/bundle_fi.properties | 4 +- core/assets/bundles/bundle_fil.properties | 4 +- core/assets/bundles/bundle_fr.properties | 4 +- core/assets/bundles/bundle_id_ID.properties | 4 +- core/assets/bundles/bundle_it.properties | 4 +- core/assets/bundles/bundle_ja.properties | 4 +- core/assets/bundles/bundle_ko.properties | 4 +- core/assets/bundles/bundle_lt.properties | 4 +- core/assets/bundles/bundle_nl.properties | 4 +- core/assets/bundles/bundle_nl_BE.properties | 4 +- core/assets/bundles/bundle_pl.properties | 4 +- core/assets/bundles/bundle_pt_BR.properties | 4 +- core/assets/bundles/bundle_pt_PT.properties | 4 +- core/assets/bundles/bundle_ro.properties | 4 +- core/assets/bundles/bundle_ru.properties | 6 +- core/assets/bundles/bundle_sr.properties | 4 +- core/assets/bundles/bundle_sv.properties | 4 +- core/assets/bundles/bundle_th.properties | 4 +- core/assets/bundles/bundle_tk.properties | 4 +- core/assets/bundles/bundle_tr.properties | 4 +- core/assets/bundles/bundle_uk_UA.properties | 4 +- core/assets/bundles/bundle_vi.properties | 2 +- core/assets/bundles/bundle_zh_CN.properties | 4 +- core/assets/bundles/bundle_zh_TW.properties | 4 +- .../mindustry/editor/MapLocalesDialog.java | 1 - .../ui/dialogs/CustomRulesDialog.java | 235 ++++++++++++------ 36 files changed, 229 insertions(+), 142 deletions(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index ef4cccb584..cbf4f022d1 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -1379,6 +1379,9 @@ rules.weather.frequency = Frequency: rules.weather.always = Always rules.weather.duration = Duration: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. + content.item.name = Items content.liquid.name = Fluids content.unit.name = Units diff --git a/core/assets/bundles/bundle_be.properties b/core/assets/bundles/bundle_be.properties index 2a0e4c4e9e..2757457626 100644 --- a/core/assets/bundles/bundle_be.properties +++ b/core/assets/bundles/bundle_be.properties @@ -598,7 +598,7 @@ filter.option.floor2 = Другая паверхню filter.option.threshold2 = Другасны гранічны парог filter.option.radius = Радыус filter.option.percentile = Процентль -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2273,7 +2273,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_bg.properties b/core/assets/bundles/bundle_bg.properties index 530c050bbd..08d2bd5893 100644 --- a/core/assets/bundles/bundle_bg.properties +++ b/core/assets/bundles/bundle_bg.properties @@ -604,7 +604,7 @@ filter.option.floor2 = Втори под filter.option.threshold2 = Втори праг filter.option.radius = Радиус filter.option.percentile = Перцентил -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2287,7 +2287,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Прочети число от свързано хранилище за памет. lst.write = Запиши число в свързано хранилище за памет. lst.print = Добави текст в буфера за изписване.\nНе визуализира нищо докато не използвате [accent]Print Flush[]. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Добавя операция в буфера за изображение.\nНе показва нищо докато не използвате [accent]Draw Flush[]. lst.drawflush = Изпълнява операции, поискани с команда [accent]Draw[] върху посочен дисплей. lst.printflush = Извежда текст натрупан с [accent]Print[] върху посочен блок за съобщение. diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index f7642061f9..bde7361028 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -607,7 +607,7 @@ filter.option.floor2 = Terra secundari filter.option.threshold2 = Llindar secundari filter.option.radius = Radi filter.option.percentile = Percentil -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2297,7 +2297,7 @@ unit.emanate.description = Construeix estructures per defensar el nucli Acròpol lst.read = Llegeix un nombre des d’una cel·la de memòria connectada. lst.write = Escriu un nombre en una cel·la de memòria connectada. lst.print = Afegeix un text a la cua d’impressió.\nEl text no es mostrarà fins que s’apliqui «[accent]Print Flush[]». -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Afegeix una instrucció de dibuix a la cua corresponent.\nEl resultat no es mostrarà fins que s’apliqui «[accent]Draw Flush[]». lst.drawflush = Executa les operacions de la cua de dibuix al monitor lògic. lst.printflush = Executa les operacions de la cua d’impressió al monitor lògic. diff --git a/core/assets/bundles/bundle_cs.properties b/core/assets/bundles/bundle_cs.properties index bd98459a70..acd90ddc68 100644 --- a/core/assets/bundles/bundle_cs.properties +++ b/core/assets/bundles/bundle_cs.properties @@ -606,7 +606,7 @@ filter.option.floor2 = Druhotný povrch filter.option.threshold2 = Druhotný práh filter.option.radius = Poloměr filter.option.percentile = Percentil -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2292,7 +2292,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Přečte číslo z připojené paměti. lst.write = Zapíše číslo do připojené paměti. lst.print = Přídá text do vypisovacího buferu.\nNezobrazí nic dokud [accent]Print Flush[] je použít. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Přídá operaci do vykreslovacího buferu.\nNezobrazí nic dokud [accent]Draw Flush[] je použít. lst.drawflush = Provede všechny [accent]Draw[] operace na zobrazovač logiky. Pak vyčistí vykreslovací bufer. lst.printflush = Provede všechny [accent]Print[] operace do zprávy. Pak vyčistí vypisovací bufer. diff --git a/core/assets/bundles/bundle_da.properties b/core/assets/bundles/bundle_da.properties index 687a4be97e..56e0977799 100644 --- a/core/assets/bundles/bundle_da.properties +++ b/core/assets/bundles/bundle_da.properties @@ -599,7 +599,7 @@ filter.option.floor2 = Sekundært gulv filter.option.threshold2 = Sekundær terskel filter.option.radius = Radius filter.option.percentile = Percentil -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2273,7 +2273,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_de.properties b/core/assets/bundles/bundle_de.properties index 023a6da38a..260236969e 100644 --- a/core/assets/bundles/bundle_de.properties +++ b/core/assets/bundles/bundle_de.properties @@ -610,7 +610,7 @@ filter.option.floor2 = Sekundärer Boden filter.option.threshold2 = Sekundärer Grenzwert filter.option.radius = Radius filter.option.percentile = Perzentil -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2322,7 +2322,7 @@ unit.emanate.description = Baut Blöcke, um den Akropolis-Kern zu beschützen. H lst.read = Liest einen Wert aus einer verbundenen Speicherzelle. lst.write = Schreibt eine Zahl in einer verbundene Speicherzelle. lst.print = Fügt Text zum Textspeicher hinzu.\nZeigt nichts an, bis [accent]Print Flush[] verwendet wird. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Fügt eine [accent]Draw[]-Aufgabe zum Bildspeicher hinzu.\nZeigt nichts an, bis [accent]Draw Flush[] verwendet wird. lst.drawflush = Druckt [accent]Draw[]-Aufgaben aus dem Bildspeicher auf einen Bildschirm. lst.printflush = Druckt [accent]Print[]-Aufgaben aus dem Textspeicher auf einen Nachrichtenblock. diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index 8e2ab61238..a1784ec9c6 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -607,7 +607,7 @@ filter.option.floor2 = Terreno secundario filter.option.threshold2 = Umbral secundario filter.option.radius = Radio filter.option.percentile = Percentil -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2315,7 +2315,7 @@ unit.emanate.description = Construye estructuras para defender el núcleo Acropo lst.read = Lee un número desde una unidad de memoria conectada. lst.write = Escribe un número en una unidad de memoria conectada. lst.print = Añade texto a la cola para imprimir texto.\nNo mostrará nada hasta que se use [accent]Print Flush[]. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Añade una operación a la cola para dibujar.\nNo mostrará nada hasta que se use [accent]Draw Flush[]. lst.drawflush = Muestra los datos en cola de operaciones [accent]Draw[] en un monitor gráfico. lst.printflush = Muestra los datos en cola de operaciones de [accent]Print[] en un bloque de mensaje. diff --git a/core/assets/bundles/bundle_et.properties b/core/assets/bundles/bundle_et.properties index 3acece8285..675204d69b 100644 --- a/core/assets/bundles/bundle_et.properties +++ b/core/assets/bundles/bundle_et.properties @@ -599,7 +599,7 @@ filter.option.floor2 = Teine põrand filter.option.threshold2 = Teine lävi filter.option.radius = Raadius filter.option.percentile = Protsentiil -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2275,7 +2275,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_eu.properties b/core/assets/bundles/bundle_eu.properties index d29722b775..e43fd2fdf6 100644 --- a/core/assets/bundles/bundle_eu.properties +++ b/core/assets/bundles/bundle_eu.properties @@ -601,7 +601,7 @@ filter.option.floor2 = Bigarren zorua filter.option.threshold2 = Bigarren atalasea filter.option.radius = Erradioa filter.option.percentile = Pertzentila -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2277,7 +2277,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_fi.properties b/core/assets/bundles/bundle_fi.properties index d2cfbbeb4a..a955c097ed 100644 --- a/core/assets/bundles/bundle_fi.properties +++ b/core/assets/bundles/bundle_fi.properties @@ -599,7 +599,7 @@ filter.option.floor2 = Toinen lattia filter.option.threshold2 = Toissijainen raja-arvo filter.option.radius = Säde filter.option.percentile = Prosentti -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2278,7 +2278,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Lue numero yhdistetystä muistisolusta. lst.write = Kirjoita numero yhdistettyyn muistisoluun. lst.print = Lisää tekstiä tekstipuskuriin.\nEi näytä mitään, kunnes [accent]Painosyötettä[] käytetään. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Lisää operaation piirtopuskuriin.\nEi näytä mitään, kunnes [accent]Piirtosyötettä[] käytetään. lst.drawflush = Syöttää jonottavat [accent]Piirto[]-operaatiot näyttöön. lst.printflush = Syöttää jonottavat [accent]Paino[]-operaatiot viestipalikkaan. diff --git a/core/assets/bundles/bundle_fil.properties b/core/assets/bundles/bundle_fil.properties index 85fe063009..08cd5e1fbd 100644 --- a/core/assets/bundles/bundle_fil.properties +++ b/core/assets/bundles/bundle_fil.properties @@ -599,7 +599,7 @@ filter.option.floor2 = Secondary Floor filter.option.threshold2 = Secondary Threshold filter.option.radius = Radius filter.option.percentile = Percentile -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2274,7 +2274,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index 0dea263859..7e3d8ea6cd 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -613,7 +613,7 @@ filter.option.floor2 = Sol secondaire filter.option.threshold2 = Seuil secondaire filter.option.radius = Rayon filter.option.percentile = Pourcentage -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2322,7 +2322,7 @@ unit.emanate.description = Construit des structures pour défendre le Noyau acro lst.read = Lit un nombre depuis un bloc de mémoire relié au processeur. lst.write = Écrit un nombre dans un bloc de mémoire relié au processeur. lst.print = Ajoute du texte dans la mémoire tampon de l'imprimante.\nNe montrera aucun texte tant que [accent]Print Flush[] ne sera pas utilisé. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Ajoute une opération dans la mémoire tampon de dessin.\nNe montrera aucune image tant que [accent]Draw Flush[] ne sera pas utilisé. lst.drawflush = Affiche les opérations [accent]Draw[] en file d'attente vers un écran. lst.printflush = Affiche les opérations [accent]Print[] en file d'attente vers un bloc de message. diff --git a/core/assets/bundles/bundle_id_ID.properties b/core/assets/bundles/bundle_id_ID.properties index f9a2127af0..0f1bd6fd5e 100644 --- a/core/assets/bundles/bundle_id_ID.properties +++ b/core/assets/bundles/bundle_id_ID.properties @@ -607,7 +607,7 @@ filter.option.floor2 = Lantai Sekunder filter.option.threshold2 = Ambang Sekunder filter.option.radius = Radius filter.option.percentile = Perseratus -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2313,7 +2313,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Membaca angka dari memori sel yang dihubungkan. lst.write = Menulis angka ke memori sel yang dihubungkan. lst.print = Menambahkan teks ke daftar cetak.\nTidak dapat menampilkan apapun sampai [accent]Print Flush[] dipakai. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Menambahkan perintah ke daftar gambar.\nTidak dapat menampilkan apapun sampai [accent]Draw Flush[] dipakai. lst.drawflush = Mengeluarkan perintah [accent]Draw[] dari daftar antrean untuk ditampilkan. lst.printflush = Mengeluarkan perintah [accent]Print[] dari daftar antrean untuk blok pesan. diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties index c5d871dd97..97221f7113 100644 --- a/core/assets/bundles/bundle_it.properties +++ b/core/assets/bundles/bundle_it.properties @@ -602,7 +602,7 @@ filter.option.floor2 = Terreno Secondario filter.option.threshold2 = Soglia Secondaria filter.option.radius = Raggio filter.option.percentile = Percentuale -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2287,7 +2287,7 @@ unit.emanate.description = Costruisce strutture per difendere il nucleo dell'Acr lst.read = Leggi un numero da una cella di memoria collegata. lst.write = Scrivi un numero in una cella di memoria collegata. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_ja.properties b/core/assets/bundles/bundle_ja.properties index 59f64d47b1..b54b1f57f3 100644 --- a/core/assets/bundles/bundle_ja.properties +++ b/core/assets/bundles/bundle_ja.properties @@ -606,7 +606,7 @@ filter.option.floor2 = 2番目の地面 filter.option.threshold2 = 2番目の閾値 filter.option.radius = 半径 filter.option.percentile = パーセンタイル -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2291,7 +2291,7 @@ unit.emanate.description = アクロポリスコアを敵から守ります。\n lst.read = リンクされたメモリセルから数値を読み取ります。 lst.write = リンクされたメモリセルに数値を書き込みます。 lst.print = メッセージブロックにテキストを追加します。[accent]Print Flush[] を使用するまで何も表示しません。 -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = ロジックディスプレイに操作を追加します。[accent]Draw Flush[] を使用するまで何も表示しません。 lst.drawflush = キューに入れられた [accent]Draw[] 操作をディスプレイにフラッシュします。 lst.printflush = キューに入れられた [accent]Print[] 操作をメッセージ ブロックにフラッシュします。 diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index f3c014faed..342c511010 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -606,7 +606,7 @@ filter.option.floor2 = 2번째 타일 filter.option.threshold2 = 2번째 경계선 filter.option.radius = 반경 filter.option.percentile = 백분율 -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2290,7 +2290,7 @@ unit.emanate.description = 코어: 도심을 지켜내기 위해 구조물을 lst.read = 연결된 메모리 셀에서 숫자 읽음 lst.write = 연결된 메모리 셀에 숫자 작성 lst.print = 프린트 버퍼에 텍스트 추가\n[accent]Print Flush[]가 사용되기 전까진 아무것도 보여주지 않습니다 -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = 드로잉 버퍼에 실행문 추가\n[accent]Draw Flush[]가 사용되기 전까진 아무것도 보여주지 않습니다 lst.drawflush = 대기중인 [accent]Draw[]실행문을 디스플레이에 출력 lst.printflush = 대기중인 [accent]Print[]실행문을 메시지 블록에 출력 diff --git a/core/assets/bundles/bundle_lt.properties b/core/assets/bundles/bundle_lt.properties index 6bc26f33fd..f61d0b08b5 100644 --- a/core/assets/bundles/bundle_lt.properties +++ b/core/assets/bundles/bundle_lt.properties @@ -599,7 +599,7 @@ filter.option.floor2 = Antrasis sluoksnis filter.option.threshold2 = Antrasis slenkstis filter.option.radius = Spindulys filter.option.percentile = Procentilė -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2275,7 +2275,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_nl.properties b/core/assets/bundles/bundle_nl.properties index 46eb33949b..66590f28e8 100644 --- a/core/assets/bundles/bundle_nl.properties +++ b/core/assets/bundles/bundle_nl.properties @@ -609,7 +609,7 @@ filter.option.floor2 = Secundaire Vloer filter.option.threshold2 = Secundaire Drempel filter.option.radius = Straal filter.option.percentile = percentiel -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2288,7 +2288,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_nl_BE.properties b/core/assets/bundles/bundle_nl_BE.properties index 01a6cf3f1f..8d0e615414 100644 --- a/core/assets/bundles/bundle_nl_BE.properties +++ b/core/assets/bundles/bundle_nl_BE.properties @@ -599,7 +599,7 @@ filter.option.floor2 = Secondary Floor filter.option.threshold2 = Secondary Threshold filter.option.radius = Radius filter.option.percentile = Percentile -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2275,7 +2275,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_pl.properties b/core/assets/bundles/bundle_pl.properties index 0de92a7351..86cea38c99 100644 --- a/core/assets/bundles/bundle_pl.properties +++ b/core/assets/bundles/bundle_pl.properties @@ -604,7 +604,7 @@ filter.option.floor2 = Druga Podłoga filter.option.threshold2 = Drugi Próg filter.option.radius = Zasięg filter.option.percentile = Procent -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2309,7 +2309,7 @@ unit.emanate.description = Lotnicza jednostka aministracyjna zdolna do wydobycia lst.read = Wczytuje liczbę z połączonej komórki pamięci. lst.write = Zapisuje liczbę do połączonej komórki pamięci. lst.print = Dodaje tekst do buforu drukującego.\nNie wyświetla niczego dopóki [accent]Print Flush[] nie jest użyte. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Dodaje operacje do buforu rysującego.\nNie wyświetla niczego dopóki [accent]Draw Flush[] nie jest użyte. lst.drawflush = Wyświetla oczekujące operacje z funkcji [accent]Draw[] na wyświetlaczu. lst.printflush = Dodaje oczekujące operacje z funkcji [accent]Print[] do bloku wiadomości. diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index 4883362261..8ac998829a 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -607,7 +607,7 @@ filter.option.floor2 = Chão secundário filter.option.threshold2 = Margem secundária filter.option.radius = Raio filter.option.percentile = Percentual -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2308,7 +2308,7 @@ unit.emanate.description = Constrói estruturas para defender o Núcelo Acrópol lst.read = Ler um número de uma célula de memória vinculada. lst.write = Escrever um número de uma célula de memória vinculada. lst.print = Adiciona texto ao buffer de impressão.\nNão exibe nada até [accent]Print Flush[] ser usado. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Adicionar uma operação ao buffer de desenho.\nNão exibe nada até [accent]Draw Flush[] ser usado. lst.drawflush = Liberar operações [accent]Draw[] enfileiradas para um display. lst.printflush = Liberar operações [accent]Print[] enfileiradas para um bloco de mensagem. diff --git a/core/assets/bundles/bundle_pt_PT.properties b/core/assets/bundles/bundle_pt_PT.properties index cf29525e4c..17ca743e25 100644 --- a/core/assets/bundles/bundle_pt_PT.properties +++ b/core/assets/bundles/bundle_pt_PT.properties @@ -599,7 +599,7 @@ filter.option.floor2 = Chão secundário filter.option.threshold2 = Margem secundária filter.option.radius = Raio filter.option.percentile = Percentual -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2275,7 +2275,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_ro.properties b/core/assets/bundles/bundle_ro.properties index d9e45b96ca..a0d08c50f9 100644 --- a/core/assets/bundles/bundle_ro.properties +++ b/core/assets/bundles/bundle_ro.properties @@ -606,7 +606,7 @@ filter.option.floor2 = Podea Secundară filter.option.threshold2 = Cantitate Secundară filter.option.radius = Rază filter.option.percentile = Procent -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2292,7 +2292,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Citește un număr dintr-o celulă de memorie conectată. lst.write = Scrie un număr într-o celulă de memorie conectată. lst.print = Adaugă text în bufferul de tipărire.\nNu tipărește decât când se execută [accent]Print Flush[]. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Adaugă o operație în bufferul de desenare.\nNu afișează decât când se execută [accent]Draw Flush[]. lst.drawflush = Afișează pe un monitor instrucțiunile [accent]Draw[] aflate în așteptare. lst.printflush = Tipărește într-un bloc Mesaj instrucțiunile [accent]Print[] aflate în așteptare. diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index c5ccfc459c..3c593f330f 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -606,7 +606,7 @@ filter.option.floor2 = Вторая поверхность filter.option.threshold2 = Вторичный предельный порог filter.option.radius = Радиус filter.option.percentile = Процентиль -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -1297,7 +1297,7 @@ rules.invaliddata = Invalid clipboard data. rules.hidebannedblocks = Скрыть запрещенные блоки rules.infiniteresources = Бесконечные ресурсы rules.onlydepositcore = Разрешен перенос только в ядро -rules.derelictrepair = Allow Derelict Block Repair +rules.derelictrepair = Разрешить починку покинутых построек rules.reactorexplosions = Взрывы реакторов rules.coreincinerates = Ядро сжигает избыток ресурсов rules.disableworldprocessors = Отключить мировые процессоры @@ -2294,7 +2294,7 @@ unit.emanate.description = Защищает ядро «Акрополь» от lst.read = Считывает число из соединённой ячейки памяти. lst.write = Записывает число в соединённую ячейку памяти. lst.print = Добавляет текст в текстовый буфер. Ничего не отображает, пока не будет вызван [accent]Print Flush[]. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Добавляет операцию в буфер отрисовки. Ничего не отображает, пока не будет вызван [accent]Draw Flush[]. lst.drawflush = Сбрасывает буфер [accent]Draw[] операций на дисплей. lst.printflush = Сбрасывает буфер [accent]Print[] операций в блок-сообщение. diff --git a/core/assets/bundles/bundle_sr.properties b/core/assets/bundles/bundle_sr.properties index 52cf175915..de821a980a 100644 --- a/core/assets/bundles/bundle_sr.properties +++ b/core/assets/bundles/bundle_sr.properties @@ -606,7 +606,7 @@ filter.option.floor2 = Drugi Pod filter.option.threshold2 = Secondary Threshold filter.option.radius = Radius filter.option.percentile = Percentile -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2295,7 +2295,7 @@ unit.emanate.description = Gradi građevine da odbrani Veliki Grad jezgro. Popra lst.read = Čita broj iz povezane memorijske ćelije. lst.write = Piše broj u povezanu memorijsku ćeliju. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_sv.properties b/core/assets/bundles/bundle_sv.properties index be714b7f2b..8d025a4cae 100644 --- a/core/assets/bundles/bundle_sv.properties +++ b/core/assets/bundles/bundle_sv.properties @@ -599,7 +599,7 @@ filter.option.floor2 = Secondary Floor filter.option.threshold2 = Secondary Threshold filter.option.radius = Radie filter.option.percentile = Percentile -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2275,7 +2275,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_th.properties b/core/assets/bundles/bundle_th.properties index 9f5f8253b8..fa345d3212 100644 --- a/core/assets/bundles/bundle_th.properties +++ b/core/assets/bundles/bundle_th.properties @@ -606,7 +606,7 @@ filter.option.floor2 = พื้นชั้นสอง filter.option.threshold2 = เกณฑ์ชั้นสอง filter.option.radius = รัศมี filter.option.percentile = เปอร์เซ็นต์ไทล์ -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2312,7 +2312,7 @@ unit.emanate.description = สร้างสิ่งต่างๆ เพื lst.read = อ่านเลขจากเซลล์ความจำที่เชื่อมต่อไว้ lst.write = เขียนเลขไปยังเซลล์ความจำที่เชื่อมต่อไว้ lst.print = เพิ่มข้อความไปยังคิวข้อความ\nข้อความจะยังไม่แสดงจนกว่าจะใช้คำสั่ง [accent]Print Flush[] -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = เพิ่มรูปไปยังคิวการวาด\nภาพจะยังไม่แสดงจนกว่าจะใช้คำสั่ง [accent]Draw Flush[] lst.drawflush = ปล่อยคิว [accent]Draw[] ไปยังหน้าจอลอจิกที่เชื่อมต่อไว้ lst.printflush = ปล่อยคิว [accent]Print[] ไปยังตัวเก็บข้อความที่เชื่อมต่อไว้ diff --git a/core/assets/bundles/bundle_tk.properties b/core/assets/bundles/bundle_tk.properties index cca3894127..63422c96b9 100644 --- a/core/assets/bundles/bundle_tk.properties +++ b/core/assets/bundles/bundle_tk.properties @@ -599,7 +599,7 @@ filter.option.floor2 = Secondary Floor filter.option.threshold2 = Secondary Threshold filter.option.radius = Radius filter.option.percentile = Percentile -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2275,7 +2275,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_tr.properties b/core/assets/bundles/bundle_tr.properties index 3df7fa80c7..ebc53ffe12 100644 --- a/core/assets/bundles/bundle_tr.properties +++ b/core/assets/bundles/bundle_tr.properties @@ -606,7 +606,7 @@ filter.option.floor2 = İkincil Duvar filter.option.threshold2 = İkincil Eşik filter.option.radius = Yarıçap filter.option.percentile = Yüzdelik -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2292,7 +2292,7 @@ unit.emanate.description = Akropolis Merkezini korumak için binalar inşa eder. lst.read = Bağlı hafıza kutusundaki numarayı okur. lst.write = Bağlı hafıza kutuaundaki numaraya yazar. lst.print = Yazı yazar. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Ekrana Çizer. lst.drawflush = Ekrana Çizimi Aktarır. lst.printflush = Mesaj bloğuna metnini aktarır, diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 6f94bbdf57..94e234dfa3 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -609,7 +609,7 @@ filter.option.floor2 = Друга поверхня filter.option.threshold2 = Вторинний граничний поріг filter.option.radius = Радіус filter.option.percentile = Спад -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2319,7 +2319,7 @@ unit.emanate.description = Англійська назва: Emanate\nБудує lst.read = Зчитує число із з’єднаної комірки пам’яті. lst.write = Записує числу у з’єднану комірку пам’яті. lst.print = Додайте текст до буфера друку.\nНічого не відображає, поки [accent]Print Flush[] використовується. -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Додає операцію до буфера рисунка.\nНічого не відображає, поки [accent]Draw Flush[] використовується. lst.drawflush = Скидає буфер операцій [accent]Draw[] на дисплей. lst.printflush = Скидає буфер операцій [accent]Print[] у блок «Повідомлення». diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index 931daa4dd9..7d74309dec 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -607,7 +607,7 @@ filter.option.floor2 = Nền phụ filter.option.threshold2 = Ngưỡng phụ filter.option.radius = Bán kính filter.option.percentile = Phần trăm -locales.info = Tại đây, bạn có thể thêm gói ngôn ngữ cho ngôn ngữ cụ thể vào bản đồ của bạn. Trong gói ngôn ngữ, mỗi thuộc tính có tên và giá trị. Những thuộc tính này có thể được sử dụng bởi Bộ xử lý thế giới và Mục tiêu nhiệm vụ bằng tên của chúng. Chúng hỗ trợ định dạng văn bản (thay thế kí tự giữ chỗ bằng giá trị thực tế).\n\n[cyan]Ví dụ thuộc tính:\n[]tên: [accent]timer[]\nvalue: [accent]Bộ đếm thời gian ví dụ, thời gian còn lại: @[]\n\n[cyan]Cách dùng:\n[]Đặt làm văn bản cho Mục tiêu nhiệm vụ: [accent]@timer\n\n[]In nó trong Bộ xử lý thế giới:\n[accent]localeprint "timer"\nformat time\n[gray](với time là biến được tính toán riêng biệt) +locales.info = Tại đây, bạn có thể thêm gói ngôn ngữ cho ngôn ngữ cụ thể vào bản đồ của bạn. Trong gói ngôn ngữ, mỗi thuộc tính có tên và giá trị. Những thuộc tính này có thể được sử dụng bởi Bộ xử lý thế giới và Mục tiêu nhiệm vụ bằng tên của chúng. Chúng hỗ trợ định dạng văn bản (thay thế kí tự giữ chỗ bằng giá trị thực tế).\n\n[cyan]Ví dụ thuộc tính:\n[]tên: [accent]timer[]\nvalue: [accent]Bộ đếm thời gian ví dụ, thời gian còn lại: {0}[]\n\n[cyan]Cách dùng:\n[]Đặt làm văn bản cho Mục tiêu nhiệm vụ: [accent]@timer\n\n[]In nó trong Bộ xử lý thế giới:\n[accent]localeprint "timer"\nformat time\n[gray](với time là biến được tính toán riêng biệt) locales.deletelocale = Bạn có chắc muốn xóa gói ngôn ngữ này? locales.applytoall = Áp dụng thay đổi cho tất cả gói ngôn ngữ locales.addtoother = Thêm vào các gói ngôn ngữ khác diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index 7386027dfa..9cc26e8dfd 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -610,7 +610,7 @@ filter.option.floor2 = 内层地形 filter.option.threshold2 = 内层比例 filter.option.radius = 半径 filter.option.percentile = 百分比 -locales.info = 在这里,您可以为特定语言添加本地化语言包到您的地图中。在本地化语言包中,每个文本属性都有一个名称和一个值。这些文本属性可以由世界处理器和游戏目标使用它们的名称。它们支持文本格式化(用实际值替换占位符)。\n\n[cyan]示例文本属性:\n[]名称: [accent]timer[]值: [accent]示例计时器, 剩余时间: @[]\n\n[cyan]用法:\n[]将其设置为目标的文本: [accent]@timer\n\n[]在世界处理器中打印它:\n[accent]localeprint "timer"\n格式化时间\n[gray](时间是一个单独计算的变量) +locales.info = 在这里,您可以为特定语言添加本地化语言包到您的地图中。在本地化语言包中,每个文本属性都有一个名称和一个值。这些文本属性可以由世界处理器和游戏目标使用它们的名称。它们支持文本格式化(用实际值替换占位符)。\n\n[cyan]示例文本属性:\n[]名称: [accent]timer[]值: [accent]示例计时器, 剩余时间: {0}[]\n\n[cyan]用法:\n[]将其设置为目标的文本: [accent]@timer\n\n[]在世界处理器中打印它:\n[accent]localeprint "timer"\n格式化时间\n[gray](时间是一个单独计算的变量) locales.deletelocale = 您确定要删除该本地化语言包吗? locales.applytoall = 将更改应用于所有本地化语言包 locales.addtoother = 添加到其他本地化语言包 @@ -2319,7 +2319,7 @@ unit.emanate.description = 保护卫城核心,可建造建筑。 使用一对 lst.read = 从连接的内存读取数字 lst.write = 向连接的内存写入数字 lst.print = 添加文字到打印缓存\n使用[accent]Print Flush[]后才会真正显示 -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = 添加绘图操作到绘图缓存\n使用[accent]Draw Flush[]后才会真正显示 lst.drawflush = 将绘图缓存中的[accent]Draw[]队列刷新到显示屏 lst.printflush = 将打印缓存中的[accent]Print[]队列刷新到信息板 diff --git a/core/assets/bundles/bundle_zh_TW.properties b/core/assets/bundles/bundle_zh_TW.properties index 85afd5a50f..5330002f2f 100644 --- a/core/assets/bundles/bundle_zh_TW.properties +++ b/core/assets/bundles/bundle_zh_TW.properties @@ -607,7 +607,7 @@ filter.option.floor2 = 次要地板 filter.option.threshold2 = 次要閾值 filter.option.radius = 半徑 filter.option.percentile = 百分比 -locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: @[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales locales.addtoother = Add To Other Locales @@ -2304,7 +2304,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = [accent]讀取[]記憶體中的一項數值 lst.write = [accent]寫入[]一項數值到記憶體中 lst.print = 將文字加入輸出的暫存中,搭配[accent]Print Flush[], [accent]Flush message[]使用 -lst.format = Replace next placeholder ("[accent]@[]") in text buffer with a value.\nExample:\n[accent]print "test @"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = 將圖形加入顯示的暫存中,搭配[accent]Draw Flush[]使用 lst.drawflush = 將所有暫存的[accent]Draw[]指令推到顯示器上 lst.printflush = 將所有暫存的[accent]Print[]指令推到訊息板上 diff --git a/core/src/mindustry/editor/MapLocalesDialog.java b/core/src/mindustry/editor/MapLocalesDialog.java index 17cc9f2764..47d902639c 100644 --- a/core/src/mindustry/editor/MapLocalesDialog.java +++ b/core/src/mindustry/editor/MapLocalesDialog.java @@ -5,7 +5,6 @@ import arc.func.*; import arc.graphics.*; import arc.scene.style.*; import arc.scene.ui.*; -import arc.scene.ui.Button.*; import arc.scene.ui.TextButton.*; import arc.scene.ui.layout.*; import arc.scene.utils.*; diff --git a/core/src/mindustry/ui/dialogs/CustomRulesDialog.java b/core/src/mindustry/ui/dialogs/CustomRulesDialog.java index 1091c74c08..40f7e1a642 100644 --- a/core/src/mindustry/ui/dialogs/CustomRulesDialog.java +++ b/core/src/mindustry/ui/dialogs/CustomRulesDialog.java @@ -3,6 +3,7 @@ package mindustry.ui.dialogs; import arc.*; import arc.func.*; import arc.graphics.*; +import arc.input.KeyCode; import arc.scene.style.*; import arc.scene.ui.*; import arc.scene.ui.ImageButton.*; @@ -30,6 +31,12 @@ public class CustomRulesDialog extends BaseDialog{ private Table main; private Prov resetter; private LoadoutDialog loadoutDialog; + public Seq categories; + public Table current; + public Seq categoryNames; + public String currentName; + public String ruleSearch = ""; + public Seq additionalSetup; // for modding to easily add new rules public CustomRulesDialog(){ super("@mode.custom"); @@ -40,6 +47,12 @@ public class CustomRulesDialog extends BaseDialog{ shown(this::setup); addCloseButton(); + additionalSetup = new Seq<>(); + categories = new Seq<>(); + categoryNames = new Seq<>(); + currentName = ""; + ruleSearch = ""; + buttons.button("@edit", Icon.pencil, () -> { BaseDialog dialog = new BaseDialog("@waves.edit"); dialog.addCloseButton(); @@ -176,13 +189,26 @@ public class CustomRulesDialog extends BaseDialog{ } void setup(){ + categories.clear(); cont.clear(); + cont.table(t -> { + t.add("@search").padRight(10); + t.field(ruleSearch, text -> + ruleSearch = text.trim().replaceAll(" +", " ").toLowerCase() + ).grow().pad(8).get().keyDown(KeyCode.enter, this::setup); + t.button(Icon.cancel, Styles.emptyi, () -> { + ruleSearch = ""; + setup(); + }).padLeft(10f).size(35f); + t.button(Icon.zoom, Styles.emptyi, this::setup).size(54f); + }).row(); cont.pane(m -> main = m).scrollX(false); main.margin(10f); - main.left().defaults().fillX().left().pad(5); + main.left().defaults().fillX().left(); main.row(); - title("@rules.title.waves"); + + category("waves"); check("@rules.waves", b -> rules.waves = b, () -> rules.waves); check("@rules.wavesending", b -> rules.waveSending = b, () -> rules.waveSending, () -> rules.waves); check("@rules.wavetimer", b -> rules.waveTimer = b, () -> rules.waveTimer, () -> rules.waves); @@ -195,7 +221,8 @@ public class CustomRulesDialog extends BaseDialog{ } number("@rules.dropzoneradius", false, f -> rules.dropZoneRadius = f * tilesize, () -> rules.dropZoneRadius / tilesize, () -> rules.waves); - title("@rules.title.resourcesbuilding"); + + category("resourcesbuilding"); check("@rules.infiniteresources", b -> { rules.infiniteResources = b; @@ -207,7 +234,7 @@ public class CustomRulesDialog extends BaseDialog{ setup(); } }, () -> rules.infiniteResources); - check("@rules.onlydepositcore", b -> rules.onlyDepositCore = b, () -> rules.onlyDepositCore); + withInfo("@rules.onlydepositcore.info", () -> check("@rules.onlydepositcore", b -> rules.onlyDepositCore = b, () -> rules.onlyDepositCore)); check("@rules.derelictrepair", b -> rules.derelictRepair = b, () -> rules.derelictRepair); check("@rules.reactorexplosions", b -> rules.reactorExplosions = b, () -> rules.reactorExplosions); check("@rules.schematic", b -> rules.schematicsAllowed = b, () -> rules.schematicsAllowed); @@ -220,18 +247,23 @@ public class CustomRulesDialog extends BaseDialog{ number("@rules.blockhealthmultiplier", f -> rules.blockHealthMultiplier = f, () -> rules.blockHealthMultiplier); number("@rules.blockdamagemultiplier", f -> rules.blockDamageMultiplier = f, () -> rules.blockDamageMultiplier); - main.button("@configure", - () -> loadoutDialog.show(999999, rules.loadout, - i -> true, - () -> rules.loadout.clear().add(new ItemStack(Items.copper, 100)), - () -> {}, () -> {} - )).left().width(300f).row(); + if(Core.bundle.get("configure").toLowerCase().contains(ruleSearch)){ + current.button("@configure", + () -> loadoutDialog.show(999999, rules.loadout, + i -> true, + () -> rules.loadout.clear().add(new ItemStack(Items.copper, 100)), + () -> {}, () -> {} + )).left().width(300f).row(); + } - main.button("@bannedblocks", () -> showBanned("@bannedblocks", ContentType.block, rules.bannedBlocks, Block::canBeBuilt)).left().width(300f).row(); + if(Core.bundle.get("bannedblocks").toLowerCase().contains(ruleSearch)){ + current.button("@bannedblocks", () -> showBanned("@bannedblocks", ContentType.block, rules.bannedBlocks, Block::canBeBuilt)).left().width(300f).row(); + } check("@rules.hidebannedblocks", b -> rules.hideBannedBlocks = b, () -> rules.hideBannedBlocks); check("@bannedblocks.whitelist", b -> rules.blockWhitelist = b, () -> rules.blockWhitelist); - title("@rules.title.unit"); + + category("unit"); check("@rules.unitcapvariable", b -> rules.unitCapVariable = b, () -> rules.unitCapVariable); check("@rules.unitpayloadsexplode", b -> rules.unitPayloadsExplode = b, () -> rules.unitPayloadsExplode); numberi("@rules.unitcap", f -> rules.unitCap = f, () -> rules.unitCap, -999, 999); @@ -241,17 +273,21 @@ public class CustomRulesDialog extends BaseDialog{ number("@rules.unitcostmultiplier", f -> rules.unitCostMultiplier = f, () -> rules.unitCostMultiplier); number("@rules.unithealthmultiplier", f -> rules.unitHealthMultiplier = f, () -> rules.unitHealthMultiplier); - main.button("@bannedunits", () -> showBanned("@bannedunits", ContentType.unit, rules.bannedUnits, u -> !u.isHidden())).left().width(300f).row(); + if(Core.bundle.get("bannedunits").toLowerCase().contains(ruleSearch)){ + current.button("@bannedunits", () -> showBanned("@bannedunits", ContentType.unit, rules.bannedUnits, u -> !u.isHidden())).left().width(300f).row(); + } check("@bannedunits.whitelist", b -> rules.unitWhitelist = b, () -> rules.unitWhitelist); - title("@rules.title.enemy"); + + category("enemy"); check("@rules.attack", b -> rules.attackMode = b, () -> rules.attackMode); check("@rules.corecapture", b -> rules.coreCapture = b, () -> rules.coreCapture); - check("@rules.placerangecheck", b -> rules.placeRangeCheck = b, () -> rules.placeRangeCheck); + withInfo("@rules.placerangecheck.info",() -> check("@rules.placerangecheck", b -> rules.placeRangeCheck = b, () -> rules.placeRangeCheck)); check("@rules.polygoncoreprotection", b -> rules.polygonCoreProtection = b, () -> rules.polygonCoreProtection); number("@rules.enemycorebuildradius", f -> rules.enemyCoreBuildRadius = f * tilesize, () -> Math.min(rules.enemyCoreBuildRadius / tilesize, 200), () -> !rules.polygonCoreProtection); - title("@rules.title.environment"); + + category("environment"); check("@rules.explosions", b -> rules.damageExplosions = b, () -> rules.damageExplosions); check("@rules.fire", b -> rules.fire = b, () -> rules.fire); check("@rules.fog", b -> rules.fog = b, () -> rules.fog); @@ -267,63 +303,70 @@ public class CustomRulesDialog extends BaseDialog{ number("@rules.solarmultiplier", f -> rules.solarMultiplier = f, () -> rules.solarMultiplier); - main.button(b -> { - b.left(); - b.table(Tex.pane, in -> { - in.stack(new Image(Tex.alphaBg), new Image(Tex.whiteui){{ - update(() -> setColor(rules.ambientLight)); - }}).grow(); - }).margin(4).size(50f).padRight(10); - b.add("@rules.ambientlight"); - }, () -> ui.picker.show(rules.ambientLight, rules.ambientLight::set)).left().width(250f).row(); + if(Core.bundle.get("rules.ambientlight").toLowerCase().contains(ruleSearch)){ + current.button(b -> { + b.left(); + b.table(Tex.pane, in -> { + in.stack(new Image(Tex.alphaBg), new Image(Tex.whiteui){{ + update(() -> setColor(rules.ambientLight)); + }}).grow(); + }).margin(4).size(50f).padRight(10); + b.add("@rules.ambientlight"); + }, () -> ui.picker.show(rules.ambientLight, rules.ambientLight::set)).left().width(250f).row(); + } - main.button("@rules.weather", this::weatherDialog).width(250f).left().row(); + if(Core.bundle.get("rules.weather").toLowerCase().contains(ruleSearch)){ + current.button("@rules.weather", this::weatherDialog).width(250f).left().row(); + } - title("@rules.title.planet"); - main.table(Tex.button, t -> { - t.margin(10f); - var group = new ButtonGroup<>(); - var style = Styles.flatTogglet; + category("planet"); + if(Core.bundle.get("rules.title.planet").toLowerCase().contains(ruleSearch)){ + current.table(Tex.button, t -> { + t.margin(10f); + var group = new ButtonGroup<>(); + var style = Styles.flatTogglet; - t.defaults().size(140f, 50f); + t.defaults().size(140f, 50f); - for(Planet planet : content.planets().select(p -> p.accessible && p.visible && p.isLandable())){ - t.button(planet.localizedName, style, () -> { - planet.applyRules(rules); - }).group(group).checked(b -> rules.planet == planet); + for(Planet planet : content.planets().select(p -> p.accessible && p.visible && p.isLandable())){ + t.button(planet.localizedName, style, () -> { + planet.applyRules(rules); + }).group(group).checked(b -> rules.planet == planet); - if(t.getChildren().size % 3 == 0){ - t.row(); + if(t.getChildren().size % 3 == 0){ + t.row(); + } } - } - t.button("@rules.anyenv", style, () -> { - rules.env = Vars.defaultEnv; - rules.hiddenBuildItems.clear(); - rules.planet = Planets.sun; - }).group(group).checked(b -> rules.planet == Planets.sun); - }).left().fill(false).expand(false, false).row(); + t.button("@rules.anyenv", style, () -> { + rules.env = Vars.defaultEnv; + rules.hiddenBuildItems.clear(); + rules.planet = Planets.sun; + }).group(group).checked(b -> rules.planet == Planets.sun); + }).left().fill(false).expand(false, false).row(); + } - title("@rules.title.teams"); + category("teams"); team("@rules.playerteam", t -> rules.defaultTeam = t, () -> rules.defaultTeam); team("@rules.enemyteam", t -> rules.waveTeam = t, () -> rules.waveTeam); for(Team team : Team.baseTeams){ boolean[] shown = {false}; - Table wasMain = main; + Table wasCurrent = current; - main.button(team.coloredName(), Icon.downOpen, Styles.togglet, () -> { + Table teamRules = new Table(); // just button and collapser in one table + teamRules.button(team.coloredName(), Icon.downOpen, Styles.togglet, () -> { shown[0] = !shown[0]; }).marginLeft(14f).width(260f).height(55f).update(t -> { ((Image)t.getChildren().get(1)).setDrawable(shown[0] ? Icon.upOpen : Icon.downOpen); t.setChecked(shown[0]); - }).row(); + }).left().padBottom(2f).row(); - main.collapser(t -> { - t.left().defaults().fillX().left().pad(5); - main = t; + teamRules.collapser(c -> { + c.left().defaults().fillX().left().pad(5); + current = c; TeamRule teams = rules.teams.get(team); number("@rules.blockhealthmultiplier", f -> teams.blockHealthMultiplier = f, () -> teams.blockHealthMultiplier); @@ -347,13 +390,42 @@ public class CustomRulesDialog extends BaseDialog{ number("@rules.unitcostmultiplier", f -> teams.unitCostMultiplier = f, () -> teams.unitCostMultiplier); number("@rules.unithealthmultiplier", f -> teams.unitHealthMultiplier = f, () -> teams.unitHealthMultiplier); - main = wasMain; - }, () -> shown[0]).growX().row(); + if(!current.hasChildren()){ + teamRules.clear(); + }else{ + wasCurrent.add(teamRules).row(); + } + + current = wasCurrent; + }, () -> shown[0]).left().growX().row(); + } + + additionalSetup.each(Runnable::run); + + for(var i = 0; i < categories.size; i++){ + addToMain(categories.get(i), Core.bundle.get("rules.title." + categoryNames.get(i))); } } - void team(String text, Cons cons, Prov prov){ - main.table(t -> { + public void category(String name){ + current = new Table(); + current.left().defaults().fillX().left().pad(5); + currentName = name; + categories.add(current); + categoryNames.add(currentName); + } + + void addToMain(Table category, String title){ + if(category.hasChildren()){ + main.add(title).color(Pal.accent).padTop(20).padRight(100f).padBottom(-3).fillX().left().pad(5).row(); + main.image().color(Pal.accent).height(3f).padRight(100f).padBottom(20).fillX().left().pad(5).row(); + main.add(category).row(); + } + } + + public void team(String text, Cons cons, Prov prov){ + if(!Core.bundle.get(text.substring(1)).toLowerCase().contains(ruleSearch)) return; + current.table(t -> { t.left(); t.add(text).left().padRight(5); @@ -365,28 +437,29 @@ public class CustomRulesDialog extends BaseDialog{ }).padTop(0).row(); } - void number(String text, Floatc cons, Floatp prov){ + public void number(String text, Floatc cons, Floatp prov){ number(text, false, cons, prov, () -> true, 0, Float.MAX_VALUE); } - void number(String text, Floatc cons, Floatp prov, float min, float max){ + public void number(String text, Floatc cons, Floatp prov, float min, float max){ number(text, false, cons, prov, () -> true, min, max); } - void number(String text, boolean integer, Floatc cons, Floatp prov, Boolp condition){ + public void number(String text, boolean integer, Floatc cons, Floatp prov, Boolp condition){ number(text, integer, cons, prov, condition, 0, Float.MAX_VALUE); } - void number(String text, Floatc cons, Floatp prov, Boolp condition){ + public void number(String text, Floatc cons, Floatp prov, Boolp condition){ number(text, false, cons, prov, condition, 0, Float.MAX_VALUE); } - void numberi(String text, Intc cons, Intp prov, int min, int max){ + public void numberi(String text, Intc cons, Intp prov, int min, int max){ numberi(text, cons, prov, () -> true, min, max); } - void numberi(String text, Intc cons, Intp prov, Boolp condition, int min, int max){ - main.table(t -> { + public void numberi(String text, Intc cons, Intp prov, Boolp condition, int min, int max){ + if(!Core.bundle.get(text.substring(1)).toLowerCase().contains(ruleSearch)) return; + current.table(t -> { t.left(); t.add(text).left().padRight(5) .update(a -> a.setColor(condition.get() ? Color.white : Color.gray)); @@ -397,8 +470,9 @@ public class CustomRulesDialog extends BaseDialog{ }).padTop(0).row(); } - void number(String text, boolean integer, Floatc cons, Floatp prov, Boolp condition, float min, float max){ - main.table(t -> { + public void number(String text, boolean integer, Floatc cons, Floatp prov, Boolp condition, float min, float max){ + if(!Core.bundle.get(text.substring(1)).toLowerCase().contains(ruleSearch)) return; + current.table(t -> { t.left(); t.add(text).left().padRight(5) .update(a -> a.setColor(condition.get() ? Color.white : Color.gray)); @@ -407,23 +481,34 @@ public class CustomRulesDialog extends BaseDialog{ .update(a -> a.setDisabled(!condition.get())) .valid(f -> Strings.canParsePositiveFloat(f) && Strings.parseFloat(f) >= min && Strings.parseFloat(f) <= max).width(120f).left(); }).padTop(0); - main.row(); + current.row(); } - void check(String text, Boolc cons, Boolp prov){ + public void check(String text, Boolc cons, Boolp prov){ check(text, cons, prov, () -> true); } - void check(String text, Boolc cons, Boolp prov, Boolp condition){ - main.check(text, cons).checked(prov.get()).update(a -> a.setDisabled(!condition.get())).padRight(100f).get().left(); - main.row(); + public void check(String text, Boolc cons, Boolp prov, Boolp condition){ + if(!Core.bundle.get(text.substring(1)).toLowerCase().contains(ruleSearch)) return; + current.check(text, cons).checked(prov.get()).update(a -> a.setDisabled(!condition.get())).padRight(100f).get().left(); + current.row(); } - void title(String text){ - main.add(text).color(Pal.accent).padTop(20).padRight(100f).padBottom(-3); - main.row(); - main.image().color(Pal.accent).height(3f).padRight(100f).padBottom(20); - main.row(); + public void withInfo(String info, Runnable add){ + Table wasCurrent = current; + current = new Table(); + current.left().defaults().fillX().left(); + + current.button(Icon.infoSmall, () -> ui.showInfo(info)).size(32f).padRight(10f); + add.run(); + + // rule does not match search pattern (runnable returned without adding anything) + if(current.getCells().size < 2){ + current.clear(); + }else{ + wasCurrent.add(current).row(); + } + current = wasCurrent; } Cell field(Table table, float value, Floatc setter){ From 7f30cd8703f500a619357872a87df1773819b97d Mon Sep 17 00:00:00 2001 From: Github Actions Date: Thu, 11 Apr 2024 21:57:25 +0000 Subject: [PATCH 095/348] Automatic bundle update --- core/assets/bundles/bundle_be.properties | 4 +++- core/assets/bundles/bundle_bg.properties | 4 +++- core/assets/bundles/bundle_ca.properties | 4 +++- core/assets/bundles/bundle_cs.properties | 4 +++- core/assets/bundles/bundle_da.properties | 4 +++- core/assets/bundles/bundle_de.properties | 4 +++- core/assets/bundles/bundle_es.properties | 4 +++- core/assets/bundles/bundle_et.properties | 4 +++- core/assets/bundles/bundle_eu.properties | 4 +++- core/assets/bundles/bundle_fi.properties | 4 +++- core/assets/bundles/bundle_fil.properties | 4 +++- core/assets/bundles/bundle_fr.properties | 4 +++- core/assets/bundles/bundle_hu.properties | 2 ++ core/assets/bundles/bundle_id_ID.properties | 4 +++- core/assets/bundles/bundle_it.properties | 4 +++- core/assets/bundles/bundle_ja.properties | 4 +++- core/assets/bundles/bundle_ko.properties | 4 +++- core/assets/bundles/bundle_lt.properties | 4 +++- core/assets/bundles/bundle_nl.properties | 4 +++- core/assets/bundles/bundle_nl_BE.properties | 4 +++- core/assets/bundles/bundle_pl.properties | 4 +++- core/assets/bundles/bundle_pt_BR.properties | 4 +++- core/assets/bundles/bundle_pt_PT.properties | 4 +++- core/assets/bundles/bundle_ro.properties | 4 +++- core/assets/bundles/bundle_ru.properties | 4 +++- core/assets/bundles/bundle_sr.properties | 4 +++- core/assets/bundles/bundle_sv.properties | 4 +++- core/assets/bundles/bundle_th.properties | 4 +++- core/assets/bundles/bundle_tk.properties | 4 +++- core/assets/bundles/bundle_tr.properties | 4 +++- core/assets/bundles/bundle_uk_UA.properties | 4 +++- core/assets/bundles/bundle_vi.properties | 2 ++ core/assets/bundles/bundle_zh_CN.properties | 4 +++- core/assets/bundles/bundle_zh_TW.properties | 4 +++- 34 files changed, 100 insertions(+), 32 deletions(-) diff --git a/core/assets/bundles/bundle_be.properties b/core/assets/bundles/bundle_be.properties index 2757457626..b1a94fc891 100644 --- a/core/assets/bundles/bundle_be.properties +++ b/core/assets/bundles/bundle_be.properties @@ -1347,6 +1347,8 @@ rules.weather = Надвор'е rules.weather.frequency = Частата: rules.weather.always = Заўсёды rules.weather.duration = Працягласць: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Рэчывы content.liquid.name = Вадкасці @@ -2273,7 +2275,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_bg.properties b/core/assets/bundles/bundle_bg.properties index 08d2bd5893..eafedcdd6b 100644 --- a/core/assets/bundles/bundle_bg.properties +++ b/core/assets/bundles/bundle_bg.properties @@ -1358,6 +1358,8 @@ rules.weather = Климат rules.weather.frequency = Честота: rules.weather.always = Винаги rules.weather.duration = Продължителност: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Предмети content.liquid.name = Течности @@ -2287,7 +2289,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Прочети число от свързано хранилище за памет. lst.write = Запиши число в свързано хранилище за памет. lst.print = Добави текст в буфера за изписване.\nНе визуализира нищо докато не използвате [accent]Print Flush[]. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Добавя операция в буфера за изображение.\nНе показва нищо докато не използвате [accent]Draw Flush[]. lst.drawflush = Изпълнява операции, поискани с команда [accent]Draw[] върху посочен дисплей. lst.printflush = Извежда текст натрупан с [accent]Print[] върху посочен блок за съобщение. diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index bde7361028..149756ce51 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -1361,6 +1361,8 @@ rules.weather = Estat meteorològic rules.weather.frequency = Freqüència: rules.weather.always = Sempre rules.weather.duration = Durada: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Elements content.liquid.name = Fluids @@ -2297,7 +2299,7 @@ unit.emanate.description = Construeix estructures per defensar el nucli Acròpol lst.read = Llegeix un nombre des d’una cel·la de memòria connectada. lst.write = Escriu un nombre en una cel·la de memòria connectada. lst.print = Afegeix un text a la cua d’impressió.\nEl text no es mostrarà fins que s’apliqui «[accent]Print Flush[]». -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Afegeix una instrucció de dibuix a la cua corresponent.\nEl resultat no es mostrarà fins que s’apliqui «[accent]Draw Flush[]». lst.drawflush = Executa les operacions de la cua de dibuix al monitor lògic. lst.printflush = Executa les operacions de la cua d’impressió al monitor lògic. diff --git a/core/assets/bundles/bundle_cs.properties b/core/assets/bundles/bundle_cs.properties index acd90ddc68..60ea6f6682 100644 --- a/core/assets/bundles/bundle_cs.properties +++ b/core/assets/bundles/bundle_cs.properties @@ -1360,6 +1360,8 @@ rules.weather = Počasí rules.weather.frequency = Četnost: rules.weather.always = Vždy rules.weather.duration = Trvání: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Předměty content.liquid.name = Kapaliny @@ -2292,7 +2294,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Přečte číslo z připojené paměti. lst.write = Zapíše číslo do připojené paměti. lst.print = Přídá text do vypisovacího buferu.\nNezobrazí nic dokud [accent]Print Flush[] je použít. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Přídá operaci do vykreslovacího buferu.\nNezobrazí nic dokud [accent]Draw Flush[] je použít. lst.drawflush = Provede všechny [accent]Draw[] operace na zobrazovač logiky. Pak vyčistí vykreslovací bufer. lst.printflush = Provede všechny [accent]Print[] operace do zprávy. Pak vyčistí vypisovací bufer. diff --git a/core/assets/bundles/bundle_da.properties b/core/assets/bundles/bundle_da.properties index 56e0977799..cb118a0b54 100644 --- a/core/assets/bundles/bundle_da.properties +++ b/core/assets/bundles/bundle_da.properties @@ -1349,6 +1349,8 @@ rules.weather = Vejr rules.weather.frequency = Frekvens: rules.weather.always = Always rules.weather.duration = Varighed: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Genstande content.liquid.name = Væsker @@ -2273,7 +2275,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_de.properties b/core/assets/bundles/bundle_de.properties index 260236969e..4713fb1e5a 100644 --- a/core/assets/bundles/bundle_de.properties +++ b/core/assets/bundles/bundle_de.properties @@ -1371,6 +1371,8 @@ rules.weather = Wetter rules.weather.frequency = Häufigkeit: rules.weather.always = Immer rules.weather.duration = Dauer: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Materialien content.liquid.name = Flüssigkeiten @@ -2322,7 +2324,7 @@ unit.emanate.description = Baut Blöcke, um den Akropolis-Kern zu beschützen. H lst.read = Liest einen Wert aus einer verbundenen Speicherzelle. lst.write = Schreibt eine Zahl in einer verbundene Speicherzelle. lst.print = Fügt Text zum Textspeicher hinzu.\nZeigt nichts an, bis [accent]Print Flush[] verwendet wird. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Fügt eine [accent]Draw[]-Aufgabe zum Bildspeicher hinzu.\nZeigt nichts an, bis [accent]Draw Flush[] verwendet wird. lst.drawflush = Druckt [accent]Draw[]-Aufgaben aus dem Bildspeicher auf einen Bildschirm. lst.printflush = Druckt [accent]Print[]-Aufgaben aus dem Textspeicher auf einen Nachrichtenblock. diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index a1784ec9c6..ce0c335822 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -1367,6 +1367,8 @@ rules.weather = Clima rules.weather.frequency = Frecuencia: rules.weather.always = Siempre rules.weather.duration = Duracion: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Objetos content.liquid.name = Líquidos @@ -2315,7 +2317,7 @@ unit.emanate.description = Construye estructuras para defender el núcleo Acropo lst.read = Lee un número desde una unidad de memoria conectada. lst.write = Escribe un número en una unidad de memoria conectada. lst.print = Añade texto a la cola para imprimir texto.\nNo mostrará nada hasta que se use [accent]Print Flush[]. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Añade una operación a la cola para dibujar.\nNo mostrará nada hasta que se use [accent]Draw Flush[]. lst.drawflush = Muestra los datos en cola de operaciones [accent]Draw[] en un monitor gráfico. lst.printflush = Muestra los datos en cola de operaciones de [accent]Print[] en un bloque de mensaje. diff --git a/core/assets/bundles/bundle_et.properties b/core/assets/bundles/bundle_et.properties index 675204d69b..6e5dc5c842 100644 --- a/core/assets/bundles/bundle_et.properties +++ b/core/assets/bundles/bundle_et.properties @@ -1349,6 +1349,8 @@ rules.weather = Weather rules.weather.frequency = Frequency: rules.weather.always = Always rules.weather.duration = Duration: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Ressursid content.liquid.name = Vedelikud @@ -2275,7 +2277,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_eu.properties b/core/assets/bundles/bundle_eu.properties index e43fd2fdf6..20dcfa9f68 100644 --- a/core/assets/bundles/bundle_eu.properties +++ b/core/assets/bundles/bundle_eu.properties @@ -1351,6 +1351,8 @@ rules.weather = Weather rules.weather.frequency = Frequency: rules.weather.always = Always rules.weather.duration = Duration: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Solidoak content.liquid.name = Likidoak @@ -2277,7 +2279,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_fi.properties b/core/assets/bundles/bundle_fi.properties index a955c097ed..b81ad76e72 100644 --- a/core/assets/bundles/bundle_fi.properties +++ b/core/assets/bundles/bundle_fi.properties @@ -1348,6 +1348,8 @@ rules.weather = Sää rules.weather.frequency = Tiheys: rules.weather.always = Aina rules.weather.duration = Kesto: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Tavarat content.liquid.name = Nesteet @@ -2278,7 +2280,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Lue numero yhdistetystä muistisolusta. lst.write = Kirjoita numero yhdistettyyn muistisoluun. lst.print = Lisää tekstiä tekstipuskuriin.\nEi näytä mitään, kunnes [accent]Painosyötettä[] käytetään. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Lisää operaation piirtopuskuriin.\nEi näytä mitään, kunnes [accent]Piirtosyötettä[] käytetään. lst.drawflush = Syöttää jonottavat [accent]Piirto[]-operaatiot näyttöön. lst.printflush = Syöttää jonottavat [accent]Paino[]-operaatiot viestipalikkaan. diff --git a/core/assets/bundles/bundle_fil.properties b/core/assets/bundles/bundle_fil.properties index 08cd5e1fbd..18b9fd2aab 100644 --- a/core/assets/bundles/bundle_fil.properties +++ b/core/assets/bundles/bundle_fil.properties @@ -1348,6 +1348,8 @@ rules.weather = Weather rules.weather.frequency = Frequency: rules.weather.always = Always rules.weather.duration = Duration: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Items content.liquid.name = Liquids @@ -2274,7 +2276,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index 7e3d8ea6cd..3ff26c05c4 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -1375,6 +1375,8 @@ rules.weather = Météo rules.weather.frequency = Fréquence : rules.weather.always = Permanent rules.weather.duration = Durée : +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Objets content.liquid.name = Liquides @@ -2322,7 +2324,7 @@ unit.emanate.description = Construit des structures pour défendre le Noyau acro lst.read = Lit un nombre depuis un bloc de mémoire relié au processeur. lst.write = Écrit un nombre dans un bloc de mémoire relié au processeur. lst.print = Ajoute du texte dans la mémoire tampon de l'imprimante.\nNe montrera aucun texte tant que [accent]Print Flush[] ne sera pas utilisé. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Ajoute une opération dans la mémoire tampon de dessin.\nNe montrera aucune image tant que [accent]Draw Flush[] ne sera pas utilisé. lst.drawflush = Affiche les opérations [accent]Draw[] en file d'attente vers un écran. lst.printflush = Affiche les opérations [accent]Print[] en file d'attente vers un bloc de message. diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index d3886064a3..b04b225ac8 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -1376,6 +1376,8 @@ rules.weather = Időjárás rules.weather.frequency = Gyakoriság: rules.weather.always = Mindig rules.weather.duration = Időtartam: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Nyersanyagok content.liquid.name = Folyadékok diff --git a/core/assets/bundles/bundle_id_ID.properties b/core/assets/bundles/bundle_id_ID.properties index 0f1bd6fd5e..e42084a3a7 100644 --- a/core/assets/bundles/bundle_id_ID.properties +++ b/core/assets/bundles/bundle_id_ID.properties @@ -1367,6 +1367,8 @@ rules.weather = Cuaca rules.weather.frequency = Frekuensi: rules.weather.always = Selalu rules.weather.duration = Durasi: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Bahan content.liquid.name = Zat Cair @@ -2313,7 +2315,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Membaca angka dari memori sel yang dihubungkan. lst.write = Menulis angka ke memori sel yang dihubungkan. lst.print = Menambahkan teks ke daftar cetak.\nTidak dapat menampilkan apapun sampai [accent]Print Flush[] dipakai. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Menambahkan perintah ke daftar gambar.\nTidak dapat menampilkan apapun sampai [accent]Draw Flush[] dipakai. lst.drawflush = Mengeluarkan perintah [accent]Draw[] dari daftar antrean untuk ditampilkan. lst.printflush = Mengeluarkan perintah [accent]Print[] dari daftar antrean untuk blok pesan. diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties index 97221f7113..6383c512ca 100644 --- a/core/assets/bundles/bundle_it.properties +++ b/core/assets/bundles/bundle_it.properties @@ -1354,6 +1354,8 @@ rules.weather = Meteo rules.weather.frequency = Frequenza: rules.weather.always = sempre rules.weather.duration = Durata: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Oggetti content.liquid.name = Liquidi @@ -2287,7 +2289,7 @@ unit.emanate.description = Costruisce strutture per difendere il nucleo dell'Acr lst.read = Leggi un numero da una cella di memoria collegata. lst.write = Scrivi un numero in una cella di memoria collegata. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_ja.properties b/core/assets/bundles/bundle_ja.properties index b54b1f57f3..ed0dc7cc69 100644 --- a/core/assets/bundles/bundle_ja.properties +++ b/core/assets/bundles/bundle_ja.properties @@ -1360,6 +1360,8 @@ rules.weather = 気象 rules.weather.frequency = 頻度: rules.weather.always = 常時 rules.weather.duration = 継続時間: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = アイテム content.liquid.name = 液体 @@ -2291,7 +2293,7 @@ unit.emanate.description = アクロポリスコアを敵から守ります。\n lst.read = リンクされたメモリセルから数値を読み取ります。 lst.write = リンクされたメモリセルに数値を書き込みます。 lst.print = メッセージブロックにテキストを追加します。[accent]Print Flush[] を使用するまで何も表示しません。 -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = ロジックディスプレイに操作を追加します。[accent]Draw Flush[] を使用するまで何も表示しません。 lst.drawflush = キューに入れられた [accent]Draw[] 操作をディスプレイにフラッシュします。 lst.printflush = キューに入れられた [accent]Print[] 操作をメッセージ ブロックにフラッシュします。 diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index 342c511010..2ec87a4548 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -1359,6 +1359,8 @@ rules.weather = 날씨 추가 rules.weather.frequency = 빈도: rules.weather.always = 항상 rules.weather.duration = 지속 시간: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = 자원 content.liquid.name = 액체 @@ -2290,7 +2292,7 @@ unit.emanate.description = 코어: 도심을 지켜내기 위해 구조물을 lst.read = 연결된 메모리 셀에서 숫자 읽음 lst.write = 연결된 메모리 셀에 숫자 작성 lst.print = 프린트 버퍼에 텍스트 추가\n[accent]Print Flush[]가 사용되기 전까진 아무것도 보여주지 않습니다 -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = 드로잉 버퍼에 실행문 추가\n[accent]Draw Flush[]가 사용되기 전까진 아무것도 보여주지 않습니다 lst.drawflush = 대기중인 [accent]Draw[]실행문을 디스플레이에 출력 lst.printflush = 대기중인 [accent]Print[]실행문을 메시지 블록에 출력 diff --git a/core/assets/bundles/bundle_lt.properties b/core/assets/bundles/bundle_lt.properties index f61d0b08b5..90cb6efbf4 100644 --- a/core/assets/bundles/bundle_lt.properties +++ b/core/assets/bundles/bundle_lt.properties @@ -1349,6 +1349,8 @@ rules.weather = Weather rules.weather.frequency = Frequency: rules.weather.always = Always rules.weather.duration = Duration: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Daiktai content.liquid.name = Skysčiai @@ -2275,7 +2277,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_nl.properties b/core/assets/bundles/bundle_nl.properties index 66590f28e8..fb69dc8371 100644 --- a/core/assets/bundles/bundle_nl.properties +++ b/core/assets/bundles/bundle_nl.properties @@ -1361,6 +1361,8 @@ rules.weather = Weer rules.weather.frequency = Frequentie: rules.weather.always = Altijd rules.weather.duration = Duur: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Materialen content.liquid.name = Vloeistoffen @@ -2288,7 +2290,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_nl_BE.properties b/core/assets/bundles/bundle_nl_BE.properties index 8d0e615414..1f345b97e0 100644 --- a/core/assets/bundles/bundle_nl_BE.properties +++ b/core/assets/bundles/bundle_nl_BE.properties @@ -1349,6 +1349,8 @@ rules.weather = Weather rules.weather.frequency = Frequency: rules.weather.always = Always rules.weather.duration = Duration: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Items content.liquid.name = Liquids @@ -2275,7 +2277,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_pl.properties b/core/assets/bundles/bundle_pl.properties index 86cea38c99..53e9cf4317 100644 --- a/core/assets/bundles/bundle_pl.properties +++ b/core/assets/bundles/bundle_pl.properties @@ -1358,6 +1358,8 @@ rules.weather = Pogoda rules.weather.frequency = Częstotliwość: rules.weather.always = Zawsze rules.weather.duration = Czas trwania: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Przedmioty content.liquid.name = Płyny @@ -2309,7 +2311,7 @@ unit.emanate.description = Lotnicza jednostka aministracyjna zdolna do wydobycia lst.read = Wczytuje liczbę z połączonej komórki pamięci. lst.write = Zapisuje liczbę do połączonej komórki pamięci. lst.print = Dodaje tekst do buforu drukującego.\nNie wyświetla niczego dopóki [accent]Print Flush[] nie jest użyte. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Dodaje operacje do buforu rysującego.\nNie wyświetla niczego dopóki [accent]Draw Flush[] nie jest użyte. lst.drawflush = Wyświetla oczekujące operacje z funkcji [accent]Draw[] na wyświetlaczu. lst.printflush = Dodaje oczekujące operacje z funkcji [accent]Print[] do bloku wiadomości. diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index 8ac998829a..4ba31c527b 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -1368,6 +1368,8 @@ rules.weather = Clima rules.weather.frequency = Frequência: rules.weather.always = Sempre rules.weather.duration = Duração: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Itens content.liquid.name = Líquidos @@ -2308,7 +2310,7 @@ unit.emanate.description = Constrói estruturas para defender o Núcelo Acrópol lst.read = Ler um número de uma célula de memória vinculada. lst.write = Escrever um número de uma célula de memória vinculada. lst.print = Adiciona texto ao buffer de impressão.\nNão exibe nada até [accent]Print Flush[] ser usado. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Adicionar uma operação ao buffer de desenho.\nNão exibe nada até [accent]Draw Flush[] ser usado. lst.drawflush = Liberar operações [accent]Draw[] enfileiradas para um display. lst.printflush = Liberar operações [accent]Print[] enfileiradas para um bloco de mensagem. diff --git a/core/assets/bundles/bundle_pt_PT.properties b/core/assets/bundles/bundle_pt_PT.properties index 17ca743e25..1780792fe3 100644 --- a/core/assets/bundles/bundle_pt_PT.properties +++ b/core/assets/bundles/bundle_pt_PT.properties @@ -1349,6 +1349,8 @@ rules.weather = Weather rules.weather.frequency = Frequency: rules.weather.always = Always rules.weather.duration = Duration: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Itens content.liquid.name = Liquidos @@ -2275,7 +2277,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_ro.properties b/core/assets/bundles/bundle_ro.properties index a0d08c50f9..b2db9b66cd 100644 --- a/core/assets/bundles/bundle_ro.properties +++ b/core/assets/bundles/bundle_ro.properties @@ -1360,6 +1360,8 @@ rules.weather = Vreme rules.weather.frequency = Frevență: rules.weather.always = Încontinuu rules.weather.duration = Durată: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Materiale content.liquid.name = Lichide @@ -2292,7 +2294,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Citește un număr dintr-o celulă de memorie conectată. lst.write = Scrie un număr într-o celulă de memorie conectată. lst.print = Adaugă text în bufferul de tipărire.\nNu tipărește decât când se execută [accent]Print Flush[]. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Adaugă o operație în bufferul de desenare.\nNu afișează decât când se execută [accent]Draw Flush[]. lst.drawflush = Afișează pe un monitor instrucțiunile [accent]Draw[] aflate în așteptare. lst.printflush = Tipărește într-un bloc Mesaj instrucțiunile [accent]Print[] aflate în așteptare. diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index 3c593f330f..ae8defc92e 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -1360,6 +1360,8 @@ rules.weather = Погода rules.weather.frequency = Периодичность: rules.weather.always = Всегда rules.weather.duration = Длительность: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Предметы content.liquid.name = Жидкости @@ -2294,7 +2296,7 @@ unit.emanate.description = Защищает ядро «Акрополь» от lst.read = Считывает число из соединённой ячейки памяти. lst.write = Записывает число в соединённую ячейку памяти. lst.print = Добавляет текст в текстовый буфер. Ничего не отображает, пока не будет вызван [accent]Print Flush[]. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Добавляет операцию в буфер отрисовки. Ничего не отображает, пока не будет вызван [accent]Draw Flush[]. lst.drawflush = Сбрасывает буфер [accent]Draw[] операций на дисплей. lst.printflush = Сбрасывает буфер [accent]Print[] операций в блок-сообщение. diff --git a/core/assets/bundles/bundle_sr.properties b/core/assets/bundles/bundle_sr.properties index de821a980a..57e10040b7 100644 --- a/core/assets/bundles/bundle_sr.properties +++ b/core/assets/bundles/bundle_sr.properties @@ -1362,6 +1362,8 @@ rules.weather = Vreme rules.weather.frequency = Učestalost: rules.weather.always = Stalno rules.weather.duration = Dužina: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Materijali content.liquid.name = Tečnosti @@ -2295,7 +2297,7 @@ unit.emanate.description = Gradi građevine da odbrani Veliki Grad jezgro. Popra lst.read = Čita broj iz povezane memorijske ćelije. lst.write = Piše broj u povezanu memorijsku ćeliju. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_sv.properties b/core/assets/bundles/bundle_sv.properties index 8d025a4cae..2268e25ca8 100644 --- a/core/assets/bundles/bundle_sv.properties +++ b/core/assets/bundles/bundle_sv.properties @@ -1349,6 +1349,8 @@ rules.weather = Weather rules.weather.frequency = Frequency: rules.weather.always = Always rules.weather.duration = Duration: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Föremål content.liquid.name = Vätskor @@ -2275,7 +2277,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_th.properties b/core/assets/bundles/bundle_th.properties index fa345d3212..91573f01ba 100644 --- a/core/assets/bundles/bundle_th.properties +++ b/core/assets/bundles/bundle_th.properties @@ -1361,6 +1361,8 @@ rules.weather = สภาพอากาศ rules.weather.frequency = ความถี่: rules.weather.always = ตลอด rules.weather.duration = ระยะเวลา: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = ไอเท็ม content.liquid.name = ของเหลว @@ -2312,7 +2314,7 @@ unit.emanate.description = สร้างสิ่งต่างๆ เพื lst.read = อ่านเลขจากเซลล์ความจำที่เชื่อมต่อไว้ lst.write = เขียนเลขไปยังเซลล์ความจำที่เชื่อมต่อไว้ lst.print = เพิ่มข้อความไปยังคิวข้อความ\nข้อความจะยังไม่แสดงจนกว่าจะใช้คำสั่ง [accent]Print Flush[] -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = เพิ่มรูปไปยังคิวการวาด\nภาพจะยังไม่แสดงจนกว่าจะใช้คำสั่ง [accent]Draw Flush[] lst.drawflush = ปล่อยคิว [accent]Draw[] ไปยังหน้าจอลอจิกที่เชื่อมต่อไว้ lst.printflush = ปล่อยคิว [accent]Print[] ไปยังตัวเก็บข้อความที่เชื่อมต่อไว้ diff --git a/core/assets/bundles/bundle_tk.properties b/core/assets/bundles/bundle_tk.properties index 63422c96b9..f2922470e8 100644 --- a/core/assets/bundles/bundle_tk.properties +++ b/core/assets/bundles/bundle_tk.properties @@ -1349,6 +1349,8 @@ rules.weather = Weather rules.weather.frequency = Frequency: rules.weather.always = Always rules.weather.duration = Duration: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Esyalar content.liquid.name = Sivilar @@ -2275,7 +2277,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. lst.print = Add text to the print buffer.\nDoes not display anything until [accent]Print Flush[] is used. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Add an operation to the drawing buffer.\nDoes not display anything until [accent]Draw Flush[] is used. lst.drawflush = Flush queued [accent]Draw[] operations to a display. lst.printflush = Flush queued [accent]Print[] operations to a message block. diff --git a/core/assets/bundles/bundle_tr.properties b/core/assets/bundles/bundle_tr.properties index ebc53ffe12..9a9ecead53 100644 --- a/core/assets/bundles/bundle_tr.properties +++ b/core/assets/bundles/bundle_tr.properties @@ -1358,6 +1358,8 @@ rules.weather = Hava Durumu rules.weather.frequency = Sıklık: rules.weather.always = Her zaman rules.weather.duration = Süreklilik: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Malzemeler content.liquid.name = Sıvılar @@ -2292,7 +2294,7 @@ unit.emanate.description = Akropolis Merkezini korumak için binalar inşa eder. lst.read = Bağlı hafıza kutusundaki numarayı okur. lst.write = Bağlı hafıza kutuaundaki numaraya yazar. lst.print = Yazı yazar. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Ekrana Çizer. lst.drawflush = Ekrana Çizimi Aktarır. lst.printflush = Mesaj bloğuna metnini aktarır, diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 94e234dfa3..24a8cf4738 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -1369,6 +1369,8 @@ rules.weather = Погода rules.weather.frequency = Повторюваність: rules.weather.always = Завжди rules.weather.duration = Тривалість: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Предмети content.liquid.name = Рідини @@ -2319,7 +2321,7 @@ unit.emanate.description = Англійська назва: Emanate\nБудує lst.read = Зчитує число із з’єднаної комірки пам’яті. lst.write = Записує числу у з’єднану комірку пам’яті. lst.print = Додайте текст до буфера друку.\nНічого не відображає, поки [accent]Print Flush[] використовується. -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = Додає операцію до буфера рисунка.\nНічого не відображає, поки [accent]Draw Flush[] використовується. lst.drawflush = Скидає буфер операцій [accent]Draw[] на дисплей. lst.printflush = Скидає буфер операцій [accent]Print[] у блок «Повідомлення». diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index 7d74309dec..6b641c3c0e 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -1362,6 +1362,8 @@ rules.weather = Thời tiết rules.weather.frequency = Tần suất: rules.weather.always = Luôn luôn rules.weather.duration = Thời gian: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = Vật phẩm content.liquid.name = Chất lỏng diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index 9cc26e8dfd..a908d005a5 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -1371,6 +1371,8 @@ rules.weather = 天气 rules.weather.frequency = 周期: rules.weather.always = 永久 rules.weather.duration = 时长: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = 物品 content.liquid.name = 液体 @@ -2319,7 +2321,7 @@ unit.emanate.description = 保护卫城核心,可建造建筑。 使用一对 lst.read = 从连接的内存读取数字 lst.write = 向连接的内存写入数字 lst.print = 添加文字到打印缓存\n使用[accent]Print Flush[]后才会真正显示 -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = 添加绘图操作到绘图缓存\n使用[accent]Draw Flush[]后才会真正显示 lst.drawflush = 将绘图缓存中的[accent]Draw[]队列刷新到显示屏 lst.printflush = 将打印缓存中的[accent]Print[]队列刷新到信息板 diff --git a/core/assets/bundles/bundle_zh_TW.properties b/core/assets/bundles/bundle_zh_TW.properties index 5330002f2f..049f543412 100644 --- a/core/assets/bundles/bundle_zh_TW.properties +++ b/core/assets/bundles/bundle_zh_TW.properties @@ -1366,6 +1366,8 @@ rules.weather = 天氣 rules.weather.frequency = 頻率: rules.weather.always = 永遠 rules.weather.duration = 持續時間: +rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. +rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. content.item.name = 物品 content.liquid.name = 液體 @@ -2304,7 +2306,7 @@ unit.emanate.description = Builds structures to defend the Acropolis core. Repai lst.read = [accent]讀取[]記憶體中的一項數值 lst.write = [accent]寫入[]一項數值到記憶體中 lst.print = 將文字加入輸出的暫存中,搭配[accent]Print Flush[], [accent]Flush message[]使用 -lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" +lst.format = Replace next placeholder in text buffer with a value.\nDoes not do anything if placeholder pattern is invalid.\nPlaceholder pattern: "{[accent]number 0-9[]}"\nExample:\n[accent]print "test {0}"\nformat "example" lst.draw = 將圖形加入顯示的暫存中,搭配[accent]Draw Flush[]使用 lst.drawflush = 將所有暫存的[accent]Draw[]指令推到顯示器上 lst.printflush = 將所有暫存的[accent]Print[]指令推到訊息板上 From 5159355cbc2de34eb3dd342931841fe1028dbad9 Mon Sep 17 00:00:00 2001 From: abcxyzDustry <138785336+abcxyzDustry@users.noreply.github.com> Date: Fri, 12 Apr 2024 20:33:48 +0700 Subject: [PATCH 096/348] I want to bring back the abcxyz server (#9734) --- servers_v7.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/servers_v7.json b/servers_v7.json index 931e0738ed..9dc9b9e811 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -242,8 +242,8 @@ "address": ["play.3midustry.xyz:6001", "play.3midustry.xyz:6002", "play.3midustry.xyz:6003", "play.3midustry.xyz:6004", "play.3midustry.xyz:6005", "play.3midustry.xyz:6006", "play.3midustry.xyz:6007", "play.3midustry.xyz:6008", "play.3midustry.xyz:6009", "play.3midustry.xyz:6010"] }, { - "name": "Siege Siege", - "address": ["157.90.4.54:25421"] + "name": "abcxyz remaster", + "address": ["157.90.4.54:25421", "144.76.57.59:21722", "51.81.166.66:29985", "144.76.57.59:24669"] }, { "name": "CroCraft Network", From 112a53c95b6e7096db020587ca999c7731f59d52 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 12 Apr 2024 19:41:09 -0400 Subject: [PATCH 097/348] Erekir turret range margin tweak --- core/src/mindustry/content/Blocks.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index e70394c328..36dfb1ee3d 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -4049,7 +4049,7 @@ public class Blocks{ researchCostMultiplier = 0.05f; coolant = consume(new ConsumeLiquid(Liquids.water, 15f / 60f)); - limitRange(); + limitRange(12f); }}; diffuse = new ItemTurret("diffuse"){{ @@ -4108,7 +4108,7 @@ public class Blocks{ rotateSpeed = 3f; coolant = consume(new ConsumeLiquid(Liquids.water, 15f / 60f)); - limitRange(); + limitRange(16f); }}; sublimate = new ContinuousLiquidTurret("sublimate"){{ @@ -4358,7 +4358,7 @@ public class Blocks{ coolant = consume(new ConsumeLiquid(Liquids.water, 20f / 60f)); coolantMultiplier = 2.5f; - limitRange(-5f); + limitRange(5f); }}; afflict = new PowerTurret("afflict"){{ From 11d7e3c7532bb68a04020a68a9ac115ce408e731 Mon Sep 17 00:00:00 2001 From: abcxyzDustry <138785336+abcxyzDustry@users.noreply.github.com> Date: Sat, 13 Apr 2024 20:21:42 +0700 Subject: [PATCH 098/348] add more ip for abcxyz server (#9741) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 9dc9b9e811..a6720c1c3e 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -243,7 +243,7 @@ }, { "name": "abcxyz remaster", - "address": ["157.90.4.54:25421", "144.76.57.59:21722", "51.81.166.66:29985", "144.76.57.59:24669"] + "address": ["157.90.4.54:25421", "144.76.57.59:21722", "51.81.166.66:29985", "144.76.57.59:24669", "23.88.73.88:15067", "23.88.73.88:15223", "144.76.57.59:25865", "23.88.73.88:15209"] }, { "name": "CroCraft Network", From 788de35f7f6e5ee00d05a908dec8574733f6d808 Mon Sep 17 00:00:00 2001 From: GaviTSRA <61122293+GaviTSRA@users.noreply.github.com> Date: Sat, 13 Apr 2024 16:00:15 +0200 Subject: [PATCH 099/348] Update servers_v7.json - Add TSR PVP and TSR Anarchy (#9743) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index a6720c1c3e..4bf64d8b48 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -1,7 +1,7 @@ [ { "name": "TSR Network", - "address": ["de-prem-01.hosts.optikservers.com:35526", "de-prem-01.hosts.optikservers.com:35915", "de-prem-01.hosts.optikservers.com:35250"] + "address": ["de-prem-01.hosts.optikservers.com:35526", "de-prem-01.hosts.optikservers.com:35915", "de-prem-01.hosts.optikservers.com:35250", "de-prem-01.hosts.optikservers.com:27526", "de-prem-01.hosts.optikservers.com:35376"] }, { "name": "Toast Mindustry", From e80a523073c2f47cee2e0e135bded05f029b7c20 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 13 Apr 2024 19:56:56 -0400 Subject: [PATCH 100/348] Rule dialog fixes --- .../ui/dialogs/CustomRulesDialog.java | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/core/src/mindustry/ui/dialogs/CustomRulesDialog.java b/core/src/mindustry/ui/dialogs/CustomRulesDialog.java index 40f7e1a642..191da9852e 100644 --- a/core/src/mindustry/ui/dialogs/CustomRulesDialog.java +++ b/core/src/mindustry/ui/dialogs/CustomRulesDialog.java @@ -3,7 +3,6 @@ package mindustry.ui.dialogs; import arc.*; import arc.func.*; import arc.graphics.*; -import arc.input.KeyCode; import arc.scene.style.*; import arc.scene.ui.*; import arc.scene.ui.ImageButton.*; @@ -193,9 +192,12 @@ public class CustomRulesDialog extends BaseDialog{ cont.clear(); cont.table(t -> { t.add("@search").padRight(10); - t.field(ruleSearch, text -> - ruleSearch = text.trim().replaceAll(" +", " ").toLowerCase() - ).grow().pad(8).get().keyDown(KeyCode.enter, this::setup); + var field = t.field(ruleSearch, text -> { + ruleSearch = text.trim().replaceAll(" +", " ").toLowerCase(); + setup(); + }).grow().pad(8).get(); + field.setCursorPosition(ruleSearch.length()); + Core.scene.setKeyboardFocus(field); t.button(Icon.cancel, Styles.emptyi, () -> { ruleSearch = ""; setup(); @@ -234,7 +236,7 @@ public class CustomRulesDialog extends BaseDialog{ setup(); } }, () -> rules.infiniteResources); - withInfo("@rules.onlydepositcore.info", () -> check("@rules.onlydepositcore", b -> rules.onlyDepositCore = b, () -> rules.onlyDepositCore)); + check("@rules.onlydepositcore", b -> rules.onlyDepositCore = b, () -> rules.onlyDepositCore); check("@rules.derelictrepair", b -> rules.derelictRepair = b, () -> rules.derelictRepair); check("@rules.reactorexplosions", b -> rules.reactorExplosions = b, () -> rules.reactorExplosions); check("@rules.schematic", b -> rules.schematicsAllowed = b, () -> rules.schematicsAllowed); @@ -282,7 +284,7 @@ public class CustomRulesDialog extends BaseDialog{ category("enemy"); check("@rules.attack", b -> rules.attackMode = b, () -> rules.attackMode); check("@rules.corecapture", b -> rules.coreCapture = b, () -> rules.coreCapture); - withInfo("@rules.placerangecheck.info",() -> check("@rules.placerangecheck", b -> rules.placeRangeCheck = b, () -> rules.placeRangeCheck)); + check("@rules.placerangecheck", b -> rules.placeRangeCheck = b, () -> rules.placeRangeCheck); check("@rules.polygoncoreprotection", b -> rules.polygonCoreProtection = b, () -> rules.polygonCoreProtection); number("@rules.enemycorebuildradius", f -> rules.enemyCoreBuildRadius = f * tilesize, () -> Math.min(rules.enemyCoreBuildRadius / tilesize, 200), () -> !rules.polygonCoreProtection); @@ -490,25 +492,13 @@ public class CustomRulesDialog extends BaseDialog{ public void check(String text, Boolc cons, Boolp prov, Boolp condition){ if(!Core.bundle.get(text.substring(1)).toLowerCase().contains(ruleSearch)) return; - current.check(text, cons).checked(prov.get()).update(a -> a.setDisabled(!condition.get())).padRight(100f).get().left(); - current.row(); - } - - public void withInfo(String info, Runnable add){ - Table wasCurrent = current; - current = new Table(); - current.left().defaults().fillX().left(); - - current.button(Icon.infoSmall, () -> ui.showInfo(info)).size(32f).padRight(10f); - add.run(); - - // rule does not match search pattern (runnable returned without adding anything) - if(current.getCells().size < 2){ - current.clear(); - }else{ - wasCurrent.add(current).row(); + String infoText = text.substring(1) + ".info"; + var cell = current.check(text, cons).checked(prov.get()).update(a -> a.setDisabled(!condition.get())).padRight(100f); + if(Core.bundle.has(infoText)){ + cell.tooltip(text + ".info"); } - current = wasCurrent; + cell.get().left(); + current.row(); } Cell field(Table table, float value, Floatc setter){ From 5c51d7dd5209c76de1b18fc0ed1576482748583d Mon Sep 17 00:00:00 2001 From: SITUVNgcd <44901211+SITUVNgcd@users.noreply.github.com> Date: Sun, 14 Apr 2024 21:06:13 +0700 Subject: [PATCH 101/348] Completely checked and transtaled Vietnamese (#9744) * Completely checked and transtaled Vietnamese I spent 1 week (few hours/day) to check every single line in **bundle** and translate: - Make `bundle_vi.properties` has same lines as original bundle file `bundle.properties`. - Retranslate some old strings/words which: + From very old versions. + Mis-translated. + Multiple meanings in Vietnamese. + Some sentences were add/removed. - Translate new strings from very last commit (https://github.com/Anuken/Mindustry/commit/7f30cd8703f500a619357872a87df1773819b97d) * Small fix First letter. --- core/assets/bundles/bundle_vi.properties | 818 ++++++++++++----------- 1 file changed, 430 insertions(+), 388 deletions(-) diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index 6b641c3c0e..11550e1885 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -8,7 +8,7 @@ link.github.description = Mã nguồn trò chơi link.changelog.description = Danh sách các thay đổi. link.dev-builds.description = Các bản dựng phát triển không ổn định link.trello.description = Bảng Trello chính thức cho các tính năng được lên kế hoạch -link.itch.io.description = itch.io page với bản tải xuống cho PC +link.itch.io.description = Trang itch.io với bản tải xuống cho PC link.google-play.description = Xem trên Google Play store link.f-droid.description = Xem trên F-Droid link.wiki.description = Mindustry wiki chính thức @@ -34,10 +34,10 @@ load.system = Hệ thống load.mod = Bản mod load.scripts = Ngữ lệnh -be.update = Đã tìm thấy bản cập nhật mới: +be.update = Đã tìm thấy bản dựng mới của Bleeding Edge. be.update.confirm = Tải xuống và khởi động lại ngay bây giờ? be.updating = Đang cập nhật... -be.ignore = Bỏ qua +be.ignore = Phớt lờ be.noupdates = Không tìm thấy bản cập nhật mới. be.check = Kiểm tra các bản cập nhật. @@ -46,8 +46,8 @@ mods.browser.selected = Mod Đã chọn mods.browser.add = Cài đặt mods.browser.reinstall = Cài đặt lại mods.browser.view-releases = Xem các bản phát hành -mods.browser.noreleases = [scarlet]Không Tìm Thấy Bản Phát Hành Nào\n[accent]Không thể tìm thấy bất cứ bản phát hành nào cho mod này. Hãy kiểm tra xem repository của mod đã có bản phát hành nào chưa. -mods.browser.latest = +mods.browser.noreleases = [scarlet]Không Tìm Thấy Bản Phát Hành Nào\n[accent]Không thể tìm thấy bất cứ bản phát hành nào cho mod này. Hãy kiểm tra xem kho lưu trữ (repo) của mod đã có bản phát hành nào chưa. +mods.browser.latest = [lightgray] [Mới nhất] mods.browser.releases = Các bản phát hành mods.github.open = Repo mods.github.open-release = Trang phát hành @@ -92,11 +92,11 @@ stats.destroyed = Số công trình bị phá hủy stats.deconstructed = Số công trình đã phá dỡ stats.playtime = Thời gian chơi -globalitems = [accent]Toàn bộ vật phẩm +globalitems = [accent]Vật phẩm của hành tinh map.delete = Bạn có chắc chắn muốn xóa bản đồ "[accent]{0}[]"? level.highscore = Điểm cao nhất: [accent]{0} level.select = Chọn cấp độ -level.mode = Chế độ: +level.mode = Chế độ chơi: coreattack = < Căn cứ đang bị tấn công! > nearpoint = [[ [scarlet]RỜI KHỎI KHU VỰC ĐÁP NGAY LẬP TỨC[] ]\nsự hủy diệt sắp xảy ra database = Cơ sở dữ liệu @@ -104,9 +104,9 @@ database.button = Cơ sở dữ liệu savegame = Lưu trò chơi loadgame = Tải lại màn chơi joingame = Tham gia trò chơi -customgame = Tùy chỉnh +customgame = Trò chơi tùy chỉnh newgame = Trò chơi mới -none = +none = none.found = [lightgray] none.inmap = [lightgray] minimap = Bản đồ nhỏ @@ -116,7 +116,7 @@ website = Trang web quit = Thoát save.quit = Lưu & Thoát maps = Bản đồ -maps.browse = Chọn bản đồ +maps.browse = Duyệt qua bản đồ continue = Tiếp tục maps.none = [lightgray]Không tìm thấy bản đồ! invalid = Không hợp lệ @@ -130,7 +130,7 @@ done = Hoàn tất feature.unsupported = Thiết bị của bạn không hỗ trợ tính năng này. mods.initfailed = [red]⚠[] Mindustry không khởi chạy được. Điều này có thể do các mod bị lỗi.\n\nĐể tránh gặp sự cố liên tiếp, [red]tất cả các mod đã bị tắt.[]\n\nĐể tắt tính năng này, vào [accent]Cài đặt->Trò chơi->Tắt các mod khi gặp sự cố trong khởi động[]. -mods = Mods +mods = Danh sách mod mods.none = [lightgray]Không tìm thấy mod! mods.guide = Hướng dẫn mod mods.report = Báo lỗi @@ -141,18 +141,20 @@ mods.reloadexit = Trò chơi sẽ đóng để mod được tải. mod.installed = [[Đã cài đặt] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Đã Bật -mod.disabled = [scarlet]Đã Tắt +mod.disabled = [red]Đã Tắt mod.multiplayer.compatible = [gray]Tương thích với chế độ nhiều người chơi mod.disable = Tắt mod.content = Nội dung: mod.delete.error = Không thể xóa mod. Tệp có thể đang được sử dụng. -mod.incompatiblegame = [red]Phiên bản trò chơi lỗi thời + +mod.incompatiblegame = [red]Trò chơi lỗi thời mod.incompatiblemod = [red]Không tương thích -mod.blacklisted = [red]Không hổ trợ +mod.blacklisted = [red]Không hỗ trợ mod.unmetdependencies = [red]Thiếu mod phụ thuộc mod.erroredcontent = [scarlet]Lỗi nội dung mod.circulardependencies = [red]Phụ thuộc tròn mod.incompletedependencies = [red]Thiếu mod phụ thuộc + mod.requiresversion.details = Yêu cầu phiên bản trò chơi: [accent]{0}[]\nPhiên bản của bạn đã lỗi thời. Mod này yêu cầu phiên bản mới hơn của trò chơi (có thể là các bản phát hành beta/alpha) để hoạt động. mod.outdatedv7.details = Mod này không tương thích với phiên bản mới nhất của trò chơi. Tác giả cần phải cập nhật nó, và thêm [accent]minGameVersion: 136[] vào tệp [accent]mod.json[]. mod.blacklisted.details = Mod này đã bị đưa vào danh sách đen do gây ra các sự cố đối với phiên bản trò chơi này. Đừng sử dụng nó. @@ -161,10 +163,11 @@ mod.erroredcontent.details = Đã xãy ra lỗi khi tải trò chơi. Vui lòng mod.circulardependencies.details = Mod này có chứa các phụ thuộc mà chính nó cũng phụ thuộc vào các mod khác. mod.incompletedependencies.details = Mod này không thể tải được do bị lỗi từ bên trong hoặc thiếu các phụ thuộc: {0}. -mod.requiresversion = Yêu cầu phiên bản game: [red]{0} +mod.requiresversion = Yêu cầu phiên bản trò chơi: [red]{0} + mod.errors = Đã xảy ra lỗi khi tải nội dung. -mod.noerrorplay = [scarlet]Bạn có mod bị lỗi.[]Tắt các mod bị lỗi hoặc sửa các lỗi trước khi chơi. -mod.nowdisabled = [scarlet]Mod '{0}' cần mod này để chạy:[accent] {1}\n[lightgray]Trước tiên bạn cần tải các mod này xuống.\nBản mod này sẽ tự động tắt. +mod.noerrorplay = [red]Bạn có mod bị lỗi.[]Tắt các mod bị lỗi hoặc sửa các lỗi trước khi chơi. +mod.nowdisabled = [red]Mod '{0}' cần mod này để chạy:[accent] {1}\n[lightgray]Trước tiên bạn cần tải các mod này xuống.\nBản mod này sẽ tự động tắt. mod.enable = Bật mod.requiresrestart = Trò chơi sẽ đóng để áp dụng các thay đổi của mod. mod.reloadrequired = [scarlet]Yêu cầu khởi động lại @@ -172,13 +175,13 @@ mod.import = Thêm Mod mod.import.file = Thêm từ tệp mod.import.github = Thêm từ GitHub mod.jarwarn = [scarlet]Các JAR mod vốn dĩ không an toàn.[]\nĐảm bảo rằng bạn đang thêm mod này từ một nguồn đáng tin cậy! -mod.item.remove = Mục này là một phần của[accent] '{0}'[] mod. Để xóa nó, hãy gỡ cài đặt mod này. +mod.item.remove = Mục này là một phần của[accent] '{0}'[] mod. Để xóa nó, hãy gỡ cài đặt mod đó. mod.remove.confirm = Mod này sẽ bị xóa. mod.author = [lightgray]Tác giả:[] {0} -mod.missing = Bản lưu này chứa các mod mà bạn đã cập nhật gần đây hoặc không được cài đặt. Có thể gây ra lỗi khi mở. Bạn có chắc muốn mở nó?\n[lightgray]Mods:\n{0} +mod.missing = Bản lưu này chứa các mod mà bạn đã cập nhật gần đây hoặc không được cài đặt. Có thể gây ra lỗi khi mở. Bạn có chắc muốn mở nó?\n[lightgray]Các mod:\n{0} mod.preview.missing = Trước khi đăng bản mod này lên workshop, bạn phải thêm hình ảnh xem trước.\nĐặt một hình ảnh có tên[accent] preview.png[] vào thư mục của mod và thử lại. mod.folder.missing = Chỉ có thể đăng các mod ở dạng thư mục lên workshop.\nĐể chuyển đổi bất kỳ mod nào thành một thư mục, chỉ cần giải nén tệp của nó vào một thư mục và xóa tệp nén cũ, sau đó khởi động lại trò chơi của bạn hoặc tải lại các bản mod của bạn. -mod.scripts.disable = Thiết bị của bạn không hổ trợ mod chứa scripts này. Bạn phải tắt các mod này để chơi trò chơi. +mod.scripts.disable = Thiết bị của bạn không hỗ trợ mod chứa các ngữ lệnh này. Bạn phải tắt các mod này để chơi trò chơi. about.button = Thông tin name = Tên: @@ -195,8 +198,8 @@ campaign.none = [lightgray]Chọn một hành tinh để bắt đầu.\nCó th campaign.erekir = Nội dung mới và được trau chuốt. Quá trình chơi liền mạch hơn.\n\nBản đồ chất lượng hơn và trải nghiệm tổng thể tốt hơn. campaign.serpulo = Nội dung cũ, trải nghiệm cơ bản. Tiến trình mở hơn.\n\nRất có thể vẫn còn bản đồ hoặc hệ thống bị mất cân bằng. Ít được trau chuốt. completed = [accent]Hoàn tất -techtree = Tiến trình -techtree.select = Chọn nhánh nghiên cứu +techtree = Cây công nghệ +techtree.select = Chọn nhánh công nghệ techtree.serpulo = Serpulo techtree.erekir = Erekir research.load = Tải @@ -210,25 +213,25 @@ players.single = {0} người chơi players.search = tìm kiếm players.notfound = [gray]không tìm thấy người chơi server.closing = [accent]Đang đóng máy chủ... -server.kicked.kick = Bạn đã bị kick khỏi máy chủ! +server.kicked.kick = Bạn đã bị đá khỏi máy chủ! server.kicked.whitelist = Bạn không nằm trong danh sách được vào máy chủ này. server.kicked.serverClose = Máy chủ đã đóng. server.kicked.vote = Bạn đã bị bỏ phiếu buộc rời phòng. Tạm biệt. -server.kicked.clientOutdated = Phiên bản máy chủ này mới hơn phiên bản trò chơi! Hãy cập nhật trò chơi của bạn! +server.kicked.clientOutdated = Phiên bản máy khách đã cũ! Hãy cập nhật trò chơi của bạn! server.kicked.serverOutdated = Phiên bản máy chủ đã cũ! Hãy yêu cầu máy chủ đó cập nhật! server.kicked.banned = Bạn đã bị cấm trên máy chủ này. -server.kicked.typeMismatch = Máy chủ này không tương thích với phiên bản của bạn. +server.kicked.typeMismatch = Máy chủ này không tương thích với kiểu bản dựng của bạn. server.kicked.playerLimit = Máy chủ đã đầy. Hãy chờ một chỗ trống. server.kicked.recentKick = Bạn đã bị buộc rời gần đây.\nHãy chờ một lúc sau đó kết nối lại. server.kicked.nameInUse = Có ai đó với cái tên này\nđã ở trong máy chủ. server.kicked.nameEmpty = Tên bạn đã chọn không hợp lệ. server.kicked.idInUse = Bạn đã ở trên máy chủ này! Bạn không được phép kết nối với hai tài khoản. -server.kicked.customClient = Máy chủ này không hổ trợ phiên bản tùy chỉnh. Hãy tải phiên bản chính thức. +server.kicked.customClient = Máy chủ này không hỗ trợ bản dựng tùy chỉnh. Hãy tải phiên bản chính thức. server.kicked.gameover = Trò chơi kết thúc! server.kicked.serverRestarting = Máy chủ đang khởi động lại. server.versions = Phiên bản của bạn:[accent] {0}[]\nPhiên bản máy chủ:[accent] {1}[] -host.info = Nút [accent]Mở máy chủ[] mở máy chủ trên cổng [scarlet]6567[]. \nBất kỳ ai trên cùng [lightgray]wifi hoặc mạng cục bộ[] sẽ có thể thấy máy chủ của bạn trong danh sách máy chủ của họ.\n\nNếu bạn muốn mọi người có thể kết nối từ mọi nơi bằng IP, [accent]port forwarding[] là bắt buộc.\n\n[lightgray]Lưu ý: Nếu ai đó đang gặp sự cố khi kết nối với máy chủ trong mạng LAN của bạn, đảm bảo rằng bạn đã cho phép Mindustry truy cập vào mạng cục bộ của mình trong cài đặt tường lửa. Lưu ý rằng các mạng công cộng đôi khi không cho phép khám phá máy chủ. -join.info = Tại đây, bạn có thể nhập [accent]IP máy chủ[] kết nối , hoặc khám phá [accent]mạng cục bộ[] hay kết nối đến máy chủ [accent]toàn cầu[].\nCả mạng LAN và WAN đều được hỗ trợ.\n\n[lightgray]Nếu bạn muốn kết nối với ai đó bằng IP, bạn sẽ cần phải hỏi IP của họ, có thể được tìm thấy bằng cách kiểm tra IP thiết bị của họ. +host.info = Nút [accent]Mở máy chủ[] mở máy chủ trên cổng [scarlet]6567[]. \nBất kỳ ai trên cùng [lightgray]wifi hoặc mạng cục bộ[] sẽ có thể thấy máy chủ của bạn trong danh sách máy chủ của họ.\n\nNếu bạn muốn mọi người có thể kết nối từ mọi nơi bằng IP, [accent]điều hướng cổng (port forwarding)[] là bắt buộc.\n\n[lightgray]Lưu ý: Nếu ai đó đang gặp sự cố khi kết nối với máy chủ trong mạng LAN của bạn, đảm bảo rằng bạn đã cho phép Mindustry truy cập vào mạng cục bộ của mình trong cài đặt tường lửa. Lưu ý rằng các mạng công cộng đôi khi không cho phép khám phá máy chủ. +join.info = Tại đây, bạn có thể nhập [accent]IP máy chủ[] kết nối , hoặc khám phá [accent]mạng cục bộ[] hay kết nối đến máy chủ [accent]toàn cầu[].\nCả mạng LAN và WAN đều được hỗ trợ.\n\n[lightgray]Nếu bạn muốn kết nối với ai đó bằng IP, bạn sẽ cần phải hỏi IP của họ, có thể được tìm thấy bằng cách tra google với từ khóa "my ip" trên thiết bị của họ. hostserver = Mở máy chủ. invitefriends = Mời bạn bè hostserver.mobile = Mở máy chủ @@ -250,12 +253,12 @@ servers.disclaimer = Nhà phát triển [accent]không[] sở hữu và kiểm s servers.showhidden = Hiển thị Máy chủ Ẩn server.shown = Hiện server.hidden = Ẩn -viewplayer = Đang xem người chơi: [accent]{0} -trace = Tìm người chơi +viewplayer = Đang xem người chơi: [accent]{0} +trace = Truy vết người chơi trace.playername = Tên người chơi: [accent]{0} trace.ip = IP: [accent]{0} -trace.id = ID: [accent]{0} +trace.id = Định danh (ID): [accent]{0} trace.language = Ngôn ngữ: [accent]{0} trace.mobile = Máy khách di động: [accent]{0} trace.modclient = Máy khách tùy chỉnh: [accent]{0} @@ -264,11 +267,13 @@ trace.times.kicked = Số lần bị buộc rời: [accent]{0} trace.ips = Các IP: trace.names = Các tên: invalidid = Định danh máy khách không hợp lệ! Vui lòng gửi báo cáo lỗi. + player.ban = Cấm -player.kick = Đá +player.kick = Buộc rời player.trace = Truy vết player.admin = Hoán đổi quản trị viên player.team = Đổi đội + server.bans = Cấm server.bans.none = Không có người chơi nào bị cấm! server.admins = Quản trị viên @@ -277,19 +282,19 @@ server.add = Thêm máy chủ server.delete = Bạn có chắc chắn muốn xóa máy chủ này không? server.edit = Chỉnh sửa máy chủ server.outdated = [scarlet]Máy chủ lỗi thời![] -server.outdated.client = [scarlet]Trò chơi lỗi thời![] +server.outdated.client = [scarlet]Máy khách lỗi thời![] server.version = [gray]v{0} {1} server.custombuild = [accent]Bản dựng tùy chỉnh confirmban = Bạn có chắc chắn muốn cấm "{0}[white]"? -confirmkick = Bạn có chắc chắn muốn buộc "{0}[white]" rời? +confirmkick = Bạn có chắc chắn muốn buộc "{0}[white]" rời đi? confirmunban = Bạn có chắc chắn muốn gỡ cấm người chơi này? confirmadmin = Bạn có chắc chắn muốn thêm "{0}[white]" làm quản trị viên? confirmunadmin = Bạn có chắc chắn muốn xóa quyền quản trị viên của "{0}[white]"? -votekick.reason = Lý do bỏ phiếu đá -votekick.reason.message = Bạn có chắc muốn bỏ phiếu đá "{0}[white]"?\nNếu có, xin hãy nhập lý do: +votekick.reason = Lý do bỏ phiếu buộc rời +votekick.reason.message = Bạn có chắc muốn bỏ phiếu buộc rời cho "{0}[white]"?\nNếu có, xin hãy nhập lý do: joingame.title = Tham gia trò chơi joingame.ip = Địa chỉ: -disconnect = Ngắt kết nối. +disconnect = Đã ngắt kết nối. disconnect.error = Lỗi kết nối. disconnect.closed = Kết nối đã bị đóng. disconnect.timeout = Hết thời gian chờ. @@ -300,14 +305,14 @@ reconnecting = [accent]Đang kết nối lại... connecting.data = [accent]Đang tải dữ liệu thế giới... server.port = Cổng: server.addressinuse = Địa chỉ đang được sử dụng! -server.invalidport = Cổng không hợp lệ! +server.invalidport = Số cổng không hợp lệ! server.error = [scarlet]Lỗi máy chủ. save.new = Bản lưu mới save.overwrite = Bạn có chắc muốn ghi đè\nbản lưu này? save.nocampaign = Các tập tin đơn lẻ từ chiến dịch không thể được nhập. overwrite = Ghi đè save.none = Không có bản lưu nào được tìm thấy! -savefail = Không thể lưu trò chơi này! +savefail = Không thể lưu trò chơi! save.delete.confirm = Bạn có chắc chắn muốn xóa bản lưu này không? save.delete = Xóa save.export = Xuất bản lưu @@ -329,7 +334,7 @@ save.search = Tìm kiếm màn chơi đã lưu... save.autosave = Tự động lưu: {0} save.map = Bản đồ: {0} save.wave = Đợt {0} -save.mode = Chế độ: {0} +save.mode = Chế độ chơi: {0} save.date = Lưu lần cuối: {0} save.playtime = Thời gian chơi: {0} warning = Cảnh báo. @@ -339,24 +344,24 @@ view.workshop = Xem trong Workshop workshop.listing = Chỉnh sửa danh sách Workshop ok = Đồng ý open = Mở -customize = Luật tùy chỉnh +customize = Quy luật tùy chỉnh cancel = Hủy command = Mệnh lệnh -command.queue = [lightgray][Đang lệnh tuần tự] +command.queue = Lệnh tuần tự command.mine = Đào -command.repair = Sửa Chữa -command.rebuild = Xây Dựng -command.assist = Hỗ Trợ Người Chơi +command.repair = Sửa chữa +command.rebuild = Xây lại +command.assist = Hỗ trợ Người chơi command.move = Di Chuyển command.boost = Tăng Cường -command.enterPayload = Nhập khối hàng vào công trình -command.loadUnits = Nhận đơn vị -command.loadBlocks = Nhận khối công trình -command.unloadPayload = Dỡ khối hàng -stance.stop = Hủy mệnh lệnh +command.enterPayload = Nhập Khối hàng vào Công trình +command.loadUnits = Nhận Đơn vị +command.loadBlocks = Nhận Khối công trình +command.unloadPayload = Dỡ Khối hàng +stance.stop = Hủy Mệnh lệnh stance.shoot = Tư thế: Bắn stance.holdfire = Tư thế: Ngừng bắn -stance.pursuetarget = Tư thế: Bám đuổi mục tiêu +stance.pursuetarget = Tư thế: Bám đuổi Mục tiêu stance.patrol = Tư thế: Đường tuần tra stance.ram = Tư thế: Tông thẳng\n[lightgray]Đi theo đường thẳng, không tìm đường openlink = Mở liên kết @@ -385,11 +390,11 @@ resumebuilding = [scarlet][[{0}][] để tiếp tục xây dựng enablebuilding = [scarlet][[{0}][] để bật xây dựng showui = Đã ẩn giao diện.\nNhấn [accent][[{0}][] để hiện lại giao diện. commandmode.name = [accent]Chế độ chỉ huy -commandmode.nounits = [no units] +commandmode.nounits = [không có đơn vị] wave = [accent]Đợt {0} wave.cap = [accent]Đợt {0}/{1} wave.waiting = [lightgray]Kẻ địch xuất hiện sau {0} -wave.waveInProgress = [lightgray]Địch đang xuất hiện. +wave.waveInProgress = [lightgray]Đợt đang tấn công waiting = [lightgray]Đang chờ... waiting.players = Đang chờ thêm người chơi... wave.enemies = [lightgray]{0} Kẻ địch còn lại @@ -402,7 +407,7 @@ loadimage = Tải hình ảnh saveimage = Lưu hình ảnh unknown = Không xác định custom = Tùy chỉnh -builtin = Xây trong +builtin = Có sẵn map.delete.confirm = Bạn có chắc chắn muốn xóa bản đồ này không? Hành động này không thể hoàn tác! map.random = [accent]Bản đồ ngẫu nhiên map.nospawn = Bản đồ này không có bất kỳ căn cứ nào để người chơi hồi sinh! Thêm một căn cứ {0} vào bản đồ ở trình chỉnh sửa. @@ -411,33 +416,33 @@ map.nospawn.attack = Bản đồ này không có bất kỳ căn cứ kẻ thù map.invalid = Lỗi khi tải bản đồ: tệp bản đồ bị hỏng hoặc không hợp lệ. workshop.update = Cập nhật mục workshop.error = Lỗi khi tìm nạp thông tin chi tiết ở workshop: {0} -map.publish.confirm = Bạn có chắc chắn muốn xuất bản bản đồ này không?\n\n[lightgray]Đảm bảo rằng bạn đồng ý với Workshop EULA trước, hoặc bản đồ của bạn sẽ không hiển thị! +map.publish.confirm = Bạn có chắc chắn muốn xuất bản bản đồ này không?\n\n[lightgray]Đảm bảo rằng bạn đồng ý với Thỏa thuận cấp phép người dùng cuối (EULA) của Workshop trước, hoặc bản đồ của bạn sẽ không hiển thị! workshop.menu = Chọn những gì bạn muốn làm với mục này. workshop.info = Thông tin mục changelog = Danh sách các thay đổi (không bắt buộc): updatedesc = Ghi đè Tiêu đề & Mô tả -eula = Steam EULA +eula = Thỏa thuận cấp phép người dùng cuối (EULA) của Steam missing = Mục này đã bị xóa hoặc di chuyển.\n[lightgray]Danh sách workshop hiện đã được tự động hủy liên kết. publishing = [accent]Đang xuất bản... -publish.confirm = Bạn có chắc chắn muốn xuất bản không?\n\n[lightgray]Đảm bảo rằng bạn đồng ý với Workshop EULA trước, hoặc mục của bạn sẽ không hiển thị! +publish.confirm = Bạn có chắc chắn muốn xuất bản không?\n\n[lightgray]Đảm bảo rằng bạn đồng ý với Thỏa thuận cấp phép người dùng cuối (EULA) của Workshop trước, hoặc mục của bạn sẽ không hiển thị! publish.error = Lỗi khi xuất bản: {0} steam.error = Không thể khởi chạy dịch vụ Steam.\nLỗi: {0} + editor.planet = Hành tinh: editor.sector = Khu vực: editor.seed = Hạt giống: - editor.cliffs = Chuyển tường thành vách đá editor.brush = Kích thước editor.openin = Mở trong trình chỉnh sửa -editor.oregen = Cấu trúc quặng -editor.oregen.info = Cấu trúc quặng: +editor.oregen = Tạo quặng +editor.oregen.info = Tạo quặng: editor.mapinfo = Thông tin bản đồ editor.author = Tác giả: editor.description = Mô tả: editor.nodescription = Bản đồ phải có mô tả ít nhất 4 ký tự trước khi được xuất bản. -editor.waves = Lượt: -editor.rules = Luật: -editor.generation = Cấu trúc: +editor.waves = Lượt +editor.rules = Quy luật +editor.generation = Tạo ra editor.objectives = Mục tiêu editor.locales = Gói ngôn ngữ editor.ingame = Chỉnh sửa trong trò chơi @@ -447,11 +452,11 @@ editor.newmap = Bản đồ mới editor.center = Trung tâm editor.search = Tìm kiếm bản đồ... editor.filters = Lọc bản đồ -editor.filters.mode = Chế độ: +editor.filters.mode = Chế độ chơi: editor.filters.type = Kiểu bản đồ: editor.filters.search = Tìm kiếm trong: editor.filters.author = Tác giả -editor.filters.description = Miêu tả +editor.filters.description = Mô tả editor.shiftx = Dịch theo X editor.shifty = Dịch theo Y workshop = Workshop @@ -463,11 +468,11 @@ waves.health = độ bền: {0}% waves.perspawn = mỗi lần xuất hiện waves.shields = khiên/đợt waves.to = đến -waves.spawn = sinh ra: +waves.spawn = xuất hiện từ: waves.spawn.all = waves.spawn.select = Chọn điểm xuất hiện waves.spawn.none = [scarlet]không tìm thấy điểm xuất hiện nào trên bản đồ -waves.max = Số lượng đơn vị tối đa +waves.max = đơn vị tối đa waves.guardian = Trùm waves.preview = Xem trước waves.edit = Chỉnh sửa... @@ -481,7 +486,7 @@ waves.sort = Sắp xếp theo waves.sort.reverse = Đảo ngược sắp xếp waves.sort.begin = Bắt đầu waves.sort.health = Độ bền -waves.sort.type = Thể loại +waves.sort.type = Kiểu loại waves.search = Tìm kiếm các lượt... waves.filter = Bộ lọc đơn vị waves.units.hide = Ẩn tất cả @@ -494,12 +499,12 @@ wavemode.health = độ bền editor.default = [lightgray] details = Chi tiết... -edit = Chỉnh sửa... +edit = Chỉnh sửa variables = Thông số logic.globals = Thông số sẵn có editor.name = Tên: -editor.spawn = Thêm kẻ địch -editor.removeunit = Xóa kẻ địch +editor.spawn = Đơn vị xuất hiện +editor.removeunit = Loại bỏ đơn vị editor.teams = Đội editor.errorload = Lỗi khi tải tệp. editor.errorsave = Lỗi khi lưu tệp. @@ -563,6 +568,7 @@ toolmode.fillerase = Điền xóa toolmode.fillerase.description = Xóa các khối cùng kiểu. toolmode.drawteams = Vẽ đội toolmode.drawteams.description = Vẽ các đội thay cho các khối. +#unused toolmode.underliquid = Dưới chất lỏng toolmode.underliquid.description = Vẽ nền dưới các ô chất lỏng. @@ -570,9 +576,9 @@ filters.empty = [lightgray]Không có bộ lọc! Thêm một cái bằng nút b filter.distort = Cong vẹo filter.noise = Nhiễu -filter.enemyspawn = Khu vực xuất hiện của kẻ thù -filter.spawnpath = Khu tạo ra -filter.corespawn = Chọn Căn cứ +filter.enemyspawn = Chọn điểm xuất hiện của kẻ thù +filter.spawnpath = Đường đến điểm xuất hiện +filter.corespawn = Chọn Lõi filter.median = Trung bình filter.oremedian = Quặng Trung bình filter.blend = Trộn @@ -581,7 +587,7 @@ filter.ore = Quặng filter.rivernoise = Nhiễu sông filter.mirror = Đối xứng filter.clear = Xóa -filter.option.ignore = Bỏ qua +filter.option.ignore = Phớt lờ filter.scatter = Phân tán filter.terrain = Địa hình @@ -590,8 +596,8 @@ filter.option.chance = Tỷ lệ filter.option.mag = Độ lớn filter.option.threshold = Ngưỡng filter.option.circle-scale = Độ lớn vòng tròn -filter.option.octaves = Octaves -filter.option.falloff = Falloff +filter.option.octaves = Rời rạc +filter.option.falloff = Phân tán filter.option.angle = Góc filter.option.tilt = Nghiêng filter.option.rotate = Quay @@ -607,7 +613,8 @@ filter.option.floor2 = Nền phụ filter.option.threshold2 = Ngưỡng phụ filter.option.radius = Bán kính filter.option.percentile = Phần trăm -locales.info = Tại đây, bạn có thể thêm gói ngôn ngữ cho ngôn ngữ cụ thể vào bản đồ của bạn. Trong gói ngôn ngữ, mỗi thuộc tính có tên và giá trị. Những thuộc tính này có thể được sử dụng bởi Bộ xử lý thế giới và Mục tiêu nhiệm vụ bằng tên của chúng. Chúng hỗ trợ định dạng văn bản (thay thế kí tự giữ chỗ bằng giá trị thực tế).\n\n[cyan]Ví dụ thuộc tính:\n[]tên: [accent]timer[]\nvalue: [accent]Bộ đếm thời gian ví dụ, thời gian còn lại: {0}[]\n\n[cyan]Cách dùng:\n[]Đặt làm văn bản cho Mục tiêu nhiệm vụ: [accent]@timer\n\n[]In nó trong Bộ xử lý thế giới:\n[accent]localeprint "timer"\nformat time\n[gray](với time là biến được tính toán riêng biệt) + +locales.info = Tại đây, bạn có thể thêm gói ngôn ngữ cho ngôn ngữ cụ thể vào bản đồ của bạn. Trong gói ngôn ngữ, mỗi thuộc tính có tên và giá trị. Những thuộc tính này có thể được sử dụng bởi Bộ xử lý thế giới và Mục tiêu nhiệm vụ bằng tên của chúng. Chúng hỗ trợ định dạng văn bản (thay thế kí tự giữ chỗ bằng giá trị thực tế).\n\n[cyan]Ví dụ thuộc tính:\n[]tên: [accent]timer[]\nvalue: [accent]Bộ đếm thời gian ví dụ, thời gian còn lại: @[]\n\n[cyan]Cách dùng:\n[]Đặt làm văn bản cho Mục tiêu nhiệm vụ: [accent]@timer\n\n[]In nó trong Bộ xử lý thế giới:\n[accent]localeprint "timer"\nformat time\n[gray](với time là biến được tính toán riêng biệt) locales.deletelocale = Bạn có chắc muốn xóa gói ngôn ngữ này? locales.applytoall = Áp dụng thay đổi cho tất cả gói ngôn ngữ locales.addtoother = Thêm vào các gói ngôn ngữ khác @@ -641,17 +648,17 @@ language.restart = Khởi động lại trò chơi của bạn để cài đặt settings = Cài đặt tutorial = Hướng dẫn tutorial.retake = Thực hiện lại Hướng dẫn -editor = Chỉnh sửa +editor = Trình chỉnh sửa mapeditor = Trình chỉnh sửa bản đồ -abandon = Bỏ +abandon = Từ bỏ abandon.text = Khu vực này và tất cả tài nguyên của nó sẽ bị mất vào tay kẻ địch. locked = Đã khóa complete = [lightgray]Hoàn thành: requirement.wave = Đạt đến đợt {0} ở {1} requirement.core = Phá hủy căn cứ địch ở {0} requirement.research = Nghiên cứu {0} -requirement.produce = Sản lượng {0} +requirement.produce = Sản xuất {0} requirement.capture = Chiếm {0} requirement.onplanet = Kiểm Soát Khu Vực {0} requirement.onsector = Đáp Xuống Khu Vực: {0} @@ -659,7 +666,8 @@ launch.text = Phóng research.multiplayer = Chỉ máy chủ mới có thể nghiên cứu các mục. map.multiplayer = Chỉ máy chủ mới có thể xem các khu vực. uncover = Khám phá -configure = Tùy chỉnh vật phẩm +configure = Cấu hình vật phẩm khởi đầu + objective.research.name = Nghiên cứu objective.produce.name = Nhận objective.item.name = Nhận vật phẩm @@ -671,17 +679,20 @@ objective.timer.name = Hẹn giờ objective.destroyblock.name = Phá huỷ khối objective.destroyblocks.name = Phá huỷ khối objective.destroycore.name = Phá huỷ căn cứ -objective.commandmode.name = Chế độ ra lệnh +objective.commandmode.name = Chế độ mệnh lệnh objective.flag.name = Cờ + marker.shapetext.name = Hình dạng văn bản marker.point.name = Điểm marker.shape.name = Hình dạng marker.text.name = Văn bản marker.line.name = Đường kẻ marker.quad.name = Bốn điểm -marker.texture.name = Texture +marker.texture.name = Kết cấu + marker.background = Nền marker.outline = Đường viền + objective.research = [accent]Nghiên cứu:\n[]{0}[lightgray]{1} objective.produce = [accent]Nhận:\n[]{0}[lightgray]{1} objective.destroyblock = [accent]Phá huỷ:\n[]{0}[lightgray]{1} @@ -695,11 +706,12 @@ objective.enemiesapproaching = [accent]Kẻ địch sẽ xuất hiện sau [ligh objective.enemyescelating = [accent]Kẻ địch sẽ bắt đầu sản xuất sau [lightgray]{0}[] objective.enemyairunits = [accent]Kẻ địch sẽ bắt đầu tạo đơn vị bay sau [lightgray]{0}[] objective.destroycore = [accent]Phá huỷ căn cứ đối phương -objective.command = [accent]Ra lệnh +objective.command = [accent]Mệnh lệnh đơn vị objective.nuclearlaunch = [accent]⚠ Phát hiện tên lửa hạt nhân sắp phóng: [lightgray]{0} -announce.nuclearstrike = [red]⚠ TÊN LỬA HẠT NHÂN SẮP VA CHẠM ⚠ -loadout = Vật phẩm +announce.nuclearstrike = [red]⚠ TÊN LỬA HẠT NHÂN SẮP VA CHẠM ⚠\nxây lõi dự phòng ngay + +loadout = Vật phẩm khởi đầu resources = Tài nguyên resources.max = Tối đa bannedblocks = Khối bị cấm @@ -719,10 +731,10 @@ connectfail = [scarlet]Lỗi kết nối:\n\n[accent]{0} error.unreachable = Không thể truy cập máy chủ.\nKiểm tra lại xem địa chỉ có đúng không? error.invalidaddress = Địa chỉ không hợp lệ. error.timedout = Hết thời gian chờ!\nĐảm bảo máy chủ đã thiết lập điều hướng cổng, và địa chỉ đó là chính xác! -error.mismatch = Lỗi packet:\nphiên bản máy khách / máy chủ có thể không khớp.\nĐảm bảo bạn và máy chủ có phiên bản Mindustry mới nhất! +error.mismatch = Lỗi gói tin:\nphiên bản máy khách / máy chủ có thể không khớp.\nĐảm bảo bạn và máy chủ có phiên bản Mindustry mới nhất! error.alreadyconnected = Đã kết nối. error.mapnotfound = Không tìm thấy tệp bản đồ! -error.io = Lỗi Network I/O. +error.io = Lỗi mạng đầu vào/ra. error.any = Lỗi mạng không xác định. error.bloom = Không khởi tạo được hiệu ứng phát sáng.\nThiết bị của bạn có thể không hỗ trợ. @@ -731,6 +743,7 @@ weather.snowing.name = Tuyết weather.sandstorm.name = Bão cát weather.sporestorm.name = Bão bào tử weather.fog.name = Sương mù + campaign.playtime = \uf129 [lightgray]Thời gian chơi trên khu vực: {0} campaign.complete = [accent]Chúc mừng.\n\nKẻ địch trên {0} đã bị đánh bại.\n[lightgray]Khu vực cuối cùng đã được chinh phục. @@ -757,10 +770,10 @@ sectors.underattack.nodamage = [scarlet]Chưa được chiếm sectors.survives = [accent]Vượt qua {0} đợt sectors.go = Đi sector.abandon = Rời bỏ -sector.abandon.confirm = Những căn cứ ở đây sẽ tự huỷ.\nTiếp tục chứ? +sector.abandon.confirm = Những lõi ở đây sẽ tự huỷ.\nTiếp tục chứ? sector.curcapture = Khu vực đã chiếm sector.curlost = Khu vực đã mất -sector.missingresources = [scarlet]Không đủ tài nguyên căn cứ +sector.missingresources = [scarlet]Không đủ tài nguyên lõi sector.attacked = Khu vực [accent]{0}[white] đang bị tấn công! sector.lost = Khu vực [accent]{0}[white] đã mất! sector.capture = Khu vực [accent]{0}[white] đã chiếm! @@ -802,23 +815,24 @@ sector.coastline.name = Coastline sector.navalFortress.name = Naval Fortress sector.groundZero.description = Vị trí tối ưu để bắt đầu một lần nữa. Mối đe dọa của kẻ thù thấp nhưng ít tài nguyên.\nThu thập càng nhiều đồng và chì càng tốt.\nTiến lên. -sector.frozenForest.description = Dù ở gần núi cao, các bào tử vẫn bắt đầu phát tán. Nhiệt độ lạnh giá không thể giữ chúng lại mãi.\n\nBắt đầu tạo năng lượng. Hãy chế tạo máy phát điện đốt và học cách sử dụng Máy sửa chữa. -sector.saltFlats.description = Ở vùng rìa sa mạc chính là Salt Flats, có thể tìm thấy một ít tài nguyên ở khu vực này.\n\nKẻ thù đã dựng lên một khu phức hợp lưu trữ tài nguyên ở đây. Hãy loại bỏ hoàn toàn căn cứ này. -sector.craters.description = Nước đã tích tụ trong miệng núi lửa ở đây, vốn là dấu tích của các cuộc chiến tranh cũ. Hãy chiếm lại khu vực. Thu gom cát, metaglass . Bơm nước để làm mát súng và mũi khoan. +sector.frozenForest.description = Dù ở gần núi cao, các bào tử vẫn bắt đầu phát tán. Nhiệt độ lạnh giá không thể giữ chúng lại mãi.\n\nBắt đầu tạo năng lượng. Hãy xây dựng máy phát điện đốt. Học cách sử dụng Máy sửa chữa. +sector.saltFlats.description = Ở vùng rìa sa mạc chính là Salt Flats. Có thể tìm thấy một ít tài nguyên ở khu vực này.\n\nKẻ thù đã dựng lên một khu phức hợp lưu trữ tài nguyên ở đây. Hãy loại bỏ hoàn toàn căn cứ này. +sector.craters.description = Nước đã tích tụ trong miệng núi lửa ở đây, vốn là dấu tích của các cuộc chiến tranh cũ. Hãy chiếm lại khu vực. Thu gom cát. Nung thủy tinh. Bơm nước để làm mát súng và mũi khoan. sector.ruinousShores.description = Vượt qua những địa hình mấp mô, là bờ biển. Vị trí này từng là nơi đặt một hệ thống phòng thủ ven biển. Không còn lại gì nhiều, chỉ những công trình phòng thủ cơ bản nhất vẫn không bị tổn thương, mọi thứ khác đều trở thành đống sắt vụn.\nTiếp tục mở rộng ra bên ngoài. Khám phá công nghệ có ở đây. sector.stainedMountains.description = Xa hơn trong đất liền là những ngọn núi, chưa bị bào tử xâm lấn.\nKhai thác titan dồi dào trong khu vực này. Tìm hiểu làm thế nào để sử dụng nó.\n\nSự hiện diện của kẻ thù ở đây lớn hơn. Đừng cho họ thời gian để có đơn vị mạnh nhất của họ. -sector.overgrowth.description = Khu vực này cây cối mọc um tùm, gần nguồn bào tử hơn.\nĐịch đã lập tiền đồn ở đây. Chế tạo Mace. Phá hủy căn cứu địch và đòi lại thứ đã mất. +sector.overgrowth.description = Khu vực này cây cối mọc um tùm, gần nguồn bào tử hơn.\nĐịch đã lập tiền đồn ở đây. Chế tạo Mace. Phá hủy căn cứu địch. sector.tarFields.description = Vùng ngoại ô của khu sản xuất dầu, nằm giữa núi và sa mạc. Một trong số ít khu vực có trữ lượng dầu có thể sử dụng được.\nMặc dù bị bỏ hoang, khu vực này có một số lực lượng địch nguy hiểm gần đó. Đừng đánh giá thấp chúng.\n\n[lightgray]Nghiên cứu công nghệ chế biến dầu nếu có thể. -sector.desolateRift.description = Một vùng cực kỳ nguy hiểm. Tài nguyên dồi dào, nhưng ít không gian. Nguy cơ thất thủ cao. Hãy rời đi càng sớm càng tốt. Đừng để bị lừa bởi khoảng cách dài giữa các cuộc tấn công của kẻ thù. +sector.desolateRift.description = Một vùng cực kỳ nguy hiểm. Tài nguyên dồi dào, nhưng ít không gian. Nguy cơ thất thủ cao. Xây dựng phòng thủ trên không và mặt đất sớm nhất có thể. Đừng để bị lừa bởi khoảng cách dài giữa các cuộc tấn công của kẻ thù. sector.nuclearComplex.description = Một cơ sở trước đây để sản xuất và chế biến thorium, đã biến thành đống đổ nát.\n[lightgray]Nghiên cứu thorium và nhiều công dụng của nó.\n\nKẻ thù có mặt ở đây với số lượng rất lớn, liên tục lùng sục những kẻ tấn công. -sector.fungalPass.description = Khu vực chuyển tiếp giữa vùng núi cao và vùng đất thấp hơn, đầy bào tử. Một căn cứ trinh sát nhỏ của địch được đặt tại đây.\nPhá hủy nó.\nSử dụng đơn vị Dagger và Crawler. Phá hủy hai căn cứ của địch. +sector.fungalPass.description = Khu vực chuyển tiếp giữa vùng núi cao và vùng đất thấp hơn, đầy bào tử. Một căn cứ trinh sát nhỏ của địch được đặt tại đây.\nPhá hủy nó.\nSử dụng đơn vị Dagger và Crawler. Phá hủy hai lõi của địch. sector.biomassFacility.description = Nguồn gốc của bào tử. Đây là cơ sở mà chúng được nghiên cứu và sản xuất ban đầu.\nNghiên cứu công nghệ có ở đây. Nuôi cấy bào tử để sản xuất nhiên liệu và chất dẻo.\n\n[lightgray]Khi cơ sở này sụp đổ, các bào tử đã được giải phóng. Không có gì trong hệ sinh thái địa phương có thể cạnh tranh với một dạng sống xâm lấn mạnh như vậy. -sector.windsweptIslands.description = Xa hơn đường bờ biển là chuỗi đảo xa xôi này. Hồ sơ cho thấy họ đã từng có công trình sản xuất [accent]Nhựa[] .\n\nChống lại các lực lượng hải quân của kẻ thù. Thiết lập căn cứ trên quần đảo. Nghiên cứu các nhà máy này. +sector.windsweptIslands.description = Xa hơn đường bờ biển là chuỗi đảo xa xôi này. Hồ sơ cho thấy họ đã từng có công trình sản xuất [accent]Nhựa[].\n\nChống lại các lực lượng hải quân của kẻ thù. Thiết lập căn cứ trên quần đảo. Nghiên cứu các nhà máy này. sector.extractionOutpost.description = Một tiền đồn xa, được kẻ thù xây dựng với mục đích phóng nguồn lực sang các khu vực khác.\n\nCông nghệ vận tải qua lại giữa các khu vực rất cần thiết để mở rộng hơn nữa. Phá hủy căn cứ và nghiên cứu bệ phóng của họ. sector.impact0078.description = Đây là tàn tích của tàu vận chuyển giữa các vì sao đã từng đến được hệ sao này.\n\nLấy càng nhiều càng tốt từ đống đổ nát. Nghiên cứu bất kỳ công nghệ nguyên vẹn nào. -sector.planetaryTerminal.description = Mục tiêu cuối cùng.\n\nCăn cứ ven biển này chứa một cấu trúc có khả năng phóng căn cứ tới các hành tinh lân cận. Nó được bảo vệ cực kỳ cẩn thận.\n\nSản xuất đơn vị hải quân. Loại bỏ kẻ thù càng nhanh càng tốt. Nghiên cứu cấu trúc phóng. +sector.planetaryTerminal.description = Mục tiêu cuối cùng.\n\nCăn cứ ven biển này chứa một cấu trúc có khả năng phóng các lõi tới các hành tinh lân cận. Nó được bảo vệ cực kỳ cẩn thận.\n\nSản xuất đơn vị hải quân. Loại bỏ kẻ thù càng nhanh càng tốt. Nghiên cứu cấu trúc phóng. sector.coastline.description = Phát hiện tàn dư công nghệ của các đơn vị hải quân tại địa điểm này. Chặn các cuộc tấn công của kẻ địch, chiếm khu vực này, và lấy được công nghệ. sector.navalFortress.description = Kẻ địch đã thiết lập một căn cứ điều kiển từ xa, trên đảo tự nhiên. Phá hủy tiền đồn này. Chiếm công nghệ chế tạo đơn vị hải quân tiên tiến của địch và nghiên cứu nó. + sector.onset.name = The Onset sector.aegis.name = Aegis sector.lake.name = Lake @@ -836,35 +850,33 @@ sector.siege.name = Siege sector.crossroads.name = Crossroads sector.karst.name = Karst sector.origin.name = Origin -sector.onset.description = Bắt đầu hành trình chinh phục Erekir. Thu thập nguồn lực, sản xuất đơn vị, và bắt đầu nghiên cứu công nghệ. +sector.onset.description = Bắt đầu hành trình chinh phục Erekir. Thu thập nguồn lực, sản xuất đơn vị, và bắt đầu nghiên cứu công nghệ. sector.aegis.description = Khu vực này chứa các kho lưu trữ của tungsten.\nNghiên cứu [accent]Máy khoan động lực[] để khai thác tài nguyên này này, và phá hủy căn cứ của địch ở khu vực này. -sector.lake.description = Hồ xỉ nóng chảy của khu vực này giới hạn rất nhiều các loại đơn vị. Một loại đơn vị có thể bay được là sự lựa chọn duy nhất.\nNghiên cứu [accent]Máy chế tạo phi thuyền[] và sản xuất một đơn vị [accent]elude[] nhanh nhất có thể. -sector.intersect.description = Các thông tin cho thấy khu vực này sẽ bị tấn công từ nhiều hướng ngay sau khi đáp.\nThiết lập các lớp phòng thủ nhanh chóng và mở rộng nhanh nhất có thể.\n[accent]Lính cơ động[] sẽ là sự lựa chọn tốt nhất cho địa hình khó khăn của khu vực này. +sector.lake.description = Hồ xỉ nóng chảy của khu vực này giới hạn rất nhiều các loại đơn vị. Một loại đơn vị có thể lướt được là sự lựa chọn duy nhất.\nNghiên cứu [accent]Máy chế tạo phi thuyền[] và sản xuất một đơn vị [accent]elude[] nhanh nhất có thể. +sector.intersect.description = Các thông tin cho thấy khu vực này sẽ bị tấn công từ nhiều hướng ngay sau khi đáp.\nThiết lập các lớp phòng thủ nhanh chóng và mở rộng nhanh nhất có thể.\n[accent]Đơn vị cơ động[] sẽ là sự lựa chọn tốt nhất cho địa hình khó khăn của khu vực này. sector.atlas.description = Khu vực này có địa hình phong phú và sẽ cần một loạt các loại đơn vị để tấn công hiệu quả.\nCác loại đơn vị được nâng cấp cũng có thể cần thiết để vượt qua một số căn cứ của địch ở đây.\nNghiên cứu [accent]Máy điện phân[] và [accent]Máy chế tạo xe tăng[]. sector.split.description = Sự hiện diện của kẻ địch ở khu vực này rất ít, nên nó là một khu vực tốt để thử nghiệm các công nghệ vận chuyển mới. -sector.basin.description = Sự hiện diện của kẻ địch ở khu vực này rất lớn.\nSản xuất đơn vị nhanh chóng và chiếm được các căn cứ của địch để có được một vị trí ổn định. +sector.basin.description = Sự hiện diện của kẻ địch ở khu vực này rất lớn.\nSản xuất đơn vị nhanh chóng và chiếm được các lõi của địch để có được một vị trí ổn định. sector.marsh.description = Khu vực này có trữ lượng lớn arkycite, nhưng có rất ít các lỗ hơi nước.\nXây dựng [accent]Bể điện hóa[] để tạo năng lượng. sector.peaks.description = Địa hình đồi núi của khu vực này khiến hầu hết các loại đơn vị vô dụng. Cần phải có các loại phi thuyền.\nHãy cẩn thận với các công trình phòng thủ trên không của địch. Có thể bị vô hiệu hóa bằng cách tấn công các công trình hỗ trợ. -sector.ravine.description = Không có căn cứ nào của địch được phát hiện ở khu vực này, nhưng nó là một đường vận chuyển quan trọng cho địch. Có thể sẽ có rất nhiều đơn vị của địch.\nSản xuất [accent]hợp kim[]. Xây dựng [accent]Afflict[]. - +sector.ravine.description = Không có căn cứ nào của địch được phát hiện ở khu vực này, nhưng nó là một đường vận chuyển quan trọng cho địch. Có thể sẽ có rất nhiều đơn vị của địch.\nSản xuất [accent]hợp kim[]. Xây dựng bệ súng [accent]Afflict[]. sector.caldera-erekir.description = Các tài nguyên được phát hiện ở khu vực này được phân bố trên nhiều đảo.\nNghiên cứu và triển khai vận chuyển dựa trên máy bay không người lái. sector.stronghold.description = Các căn cứ của địch ở khu vực này đang bảo vệ một lượng lớn [accent]thorium[].\nSử dụng nó để phát triển các loại đơn vị và công trình tốt hơn. - sector.crevice.description = Địch sẽ gửi các đơn vị tấn công mạnh mẽ để tiêu diệt căn cứ của bạn ở khu vực này.\nPhát triển [accent]carbide[] và [accent]Máy nhiệt phân[] là điều cần thiết để sống sót. -sector.siege.description = Khu vực này có hai hẻm núi song song với nhau, kẻ địch sẽ tấn công từ hai phía.\nNghiên cứu [accent]cyanogen[] để có thể chế tạo những xe tank mạnh hơn.\nChú ý: Phát hiện kẻ địch được trang bị tên lửa tầm xa. Các tên lửa có thể bị bắn hạ trước khi va chạm. +sector.siege.description = Khu vực này có hai hẻm núi song song với nhau, kẻ địch sẽ tấn công từ hai phía.\nNghiên cứu [accent]cyanogen[] để có thể chế tạo những đơn vị xe tăng mạnh hơn.\nChú ý: Phát hiện kẻ địch được trang bị tên lửa tầm xa. Các tên lửa có thể bị bắn hạ trước khi va chạm. sector.crossroads.description = Các căn cứ của địch ở khu vực này được xây dựng trên các địa hình khác nhau. Nghiên cứu các loại đơn vị khác nhau sao cho phù hợp.\nNgoài ra, một số căn cứ được bảo vệ bởi các máy chiếu khiên chắn. Tìm hiểu cách chúng được cung cấp năng lượng. sector.karst.description = Khu vực này có rất nhiều tài nguyên, nhưng sẽ bị địch tấn công khi một căn cứ mới đáp.\nTận dụng các tài nguyên và nghiên cứu [accent]sợi lượng tử[]. sector.origin.description = Khu vực cuối cùng với sự hiện diện của địch rất lớn.\nKhông còn bất cứ thứ gì cần nghiên cứu - tập trung hoàn toàn vào việc tiêu diệt tất cả các căn cứ của địch. status.burning.name = Cháy status.freezing.name = Đóng băng -status.wet.name = Ẩm +status.wet.name = Ẩm ướt status.muddy.name = Sa lầy status.melting.name = Tan chảy status.sapped.name = Ăn mòn status.electrified.name = Nhiễm điện -status.spore-slowed.name = Nhiễm bào tử +status.spore-slowed.name = Nhiễm bào tử chậm status.tarred.name = Nhiễm dầu status.overdrive.name = Tăng cường status.overclock.name = Gia tốc @@ -876,7 +888,7 @@ status.boss.name = Trùm settings.language = Ngôn ngữ settings.data = Dữ liệu trò chơi settings.reset = Khôi phục về mặc định -settings.rebind = Sửa +settings.rebind = Gán lại settings.resetKey = Đặt lại settings.controls = Điều khiển settings.game = Trò chơi @@ -884,7 +896,7 @@ settings.sound = Âm thanh settings.graphics = Đồ họa settings.cleardata = Xóa dữ liệu trò chơi... settings.clear.confirm = Bạn có chắc chắn muốn xóa dữ liệu này không?\nHành động này không thể hoàn tác! -settings.clearall.confirm = [scarlet]CẢNH BÁO![]\nThao tác này sẽ xóa tất cả dữ liệu, bao gồm bản đồ và các cài đặt.\nSau khi bạn nhấn vào 'ok' trò chơi sẽ xóa tất cả dữ liệu và tự động thoát. +settings.clearall.confirm = [scarlet]CẢNH BÁO![]\nThao tác này sẽ xóa tất cả dữ liệu, bao gồm bản đồ và các cài đặt.\nSau khi bạn nhấn vào 'Đồng ý' trò chơi sẽ xóa tất cả dữ liệu và tự động thoát. settings.clearsaves.confirm = Bạn có chắc muốn xóa tất cả bản lưu? settings.clearsaves = Xóa bản lưu settings.clearresearch = Xóa dữ liệu nghiên cứu @@ -893,7 +905,7 @@ settings.clearcampaignsaves = Xóa dữ liệu Chiến dịch settings.clearcampaignsaves.confirm = Bạn có chắc muốn xóa toàn bộ dữ liệu Chiến dịch? paused = [accent]< Tạm dừng > clear = Xóa -banned = [scarlet]Cấm +banned = [scarlet]Bị cấm unsupported.environment = [scarlet]Môi trường không phù hợp yes = Có no = Không @@ -905,15 +917,15 @@ lastaccessed = [lightgray]Truy cập lần cuối: {0} lastcommanded = [lightgray]Được điều khiển lần cuối: {0} block.unknown = [lightgray]??? -stat.showinmap = -stat.description = Mô tả +stat.showinmap = +stat.description = Mục đích stat.input = Đầu vào stat.output = Sản phẩm stat.maxefficiency = Hiệu suất tối đa stat.booster = Tăng cường -stat.tiles = Yêu cầu khu vực +stat.tiles = Yêu cầu ô nền stat.affinities = Phù hợp -stat.opposites = Đối diện +stat.opposites = Đối nghịch stat.powercapacity = Dung lượng pin stat.powershot = Năng lượng/Phát bắn stat.damage = Sát thương @@ -926,21 +938,21 @@ stat.size = Kích thước stat.displaysize = Kích thước màn hình stat.liquidcapacity = Dung tích chất lỏng stat.powerrange = Phạm vi năng lượng -stat.linkrange = Phạm vi kết nối +stat.linkrange = Phạm vi liên kết stat.instructions = Hướng dẫn stat.powerconnections = Số lượng kết nối tối đa stat.poweruse = Năng lượng sử dụng stat.powerdamage = Năng lượng/Sát thương stat.itemcapacity = Sức chứa vật phẩm stat.memorycapacity = Dung lượng bộ nhớ -stat.basepowergeneration = Năng lượng tạo ra (cơ bản) +stat.basepowergeneration = Năng lượng tạo ra cơ bản stat.productiontime = Thời gian sản xuất stat.repairtime = Thời gian sửa stat.repairspeed = Tốc độ sửa stat.weapons = Vũ khí stat.bullet = Đạn stat.moduletier = Cấp Module -stat.unittype = Unit Type +stat.unittype = Kiểu đơn vị stat.speedincrease = Tăng tốc stat.range = Phạm vi stat.drilltier = Khoan được @@ -951,13 +963,13 @@ stat.health = Độ bền stat.armor = Giáp stat.buildtime = Thời gian xây stat.maxconsecutive = Đầu ra tối đa -stat.buildcost = Yêu cầu +stat.buildcost = Chi phi xây stat.inaccuracy = Độ lệch stat.shots = Phát bắn -stat.reload = Phát bắn/Giây +stat.reload = Tốc độ bắn stat.ammo = Đạn stat.shieldhealth = Độ bền khiên -stat.cooldowntime = Thời gian chờ +stat.cooldowntime = Thời gian hồi phục stat.explosiveness = Gây nổ stat.basedeflectchance = Tỷ lệ phản đạn stat.lightningchance = Tỷ lệ phóng điện @@ -974,23 +986,23 @@ stat.minespeed = Tốc độ đào stat.minetier = Cấp độ đào stat.payloadcapacity = Sức chứa khối hàng stat.abilities = Khả năng -stat.canboost = Nâng cấp +stat.canboost = Có thê tăng cường stat.flying = Bay stat.ammouse = Sử dụng đạn stat.damagemultiplier = Hệ số sát thương stat.healthmultiplier = Hệ số độ bền stat.speedmultiplier = Hệ số tốc độ -stat.reloadmultiplier = Hệ số tốc độ tấn công +stat.reloadmultiplier = Hệ số hồi đạn stat.buildspeedmultiplier = Hệ số tốc độ xây dựng stat.reactive = Phản ứng stat.immunities = Miễn nhiễm -stat.healing = Sửa chữa +stat.healing = Hồi phục ability.forcefield = Tạo khiên ability.forcefield.description = Phát một khiên trường lực hấp thụ các loại đạn ability.repairfield = Sửa chữa/Xây dựng ability.repairfield.description = Sửa chữa các đơn vị gần đó -ability.statusfield = Vùng gia tốc +ability.statusfield = Vùng trạng thái ability.statusfield.description = Áp dụng hiệu ứng trạng thái vào các đơn vị gần đó ability.unitspawn = Sản xuất ability.unitspawn.description = Sản xuất đơn vị @@ -1007,15 +1019,16 @@ ability.suppressionfield.description = Ngăn chặn các công trình sửa ch ability.energyfield = Trường điện từ ability.energyfield.description = Giật điện các kẻ thù gần đó ability.energyfield.healdescription = Giật điện các kẻ thù gần đó và hồi phục đồng minh -ability.regen = Hồi phục -ability.regen.description = Tự hồi phục theo thời gian +ability.regen = Tự hồi phục +ability.regen.description = Tự hồi phục độ bền theo thời gian ability.liquidregen = Hấp thụ chất lỏng ability.liquidregen.description = Hấp thụ chất lỏng để tự hồi phục ability.spawndeath = Chết sản sinh ability.spawndeath.description = Sinh ra đơn vị khi chết ability.liquidexplode = Chết tràn dịch ability.liquidexplode.description = Tràn chất lỏng khi chết -ability.stat.firingrate = tốc bắn [stat]{0}/giây[lightgray] + +ability.stat.firingrate = tốc độ bắn [stat]{0}/giây[lightgray] ability.stat.regen = [stat]{0}[lightgray] độ bền/giây ability.stat.shield = [stat]{0}[lightgray] khiên ability.stat.repairspeed = [stat]{0}/giây[lightgray] tốc độ sửa chữa @@ -1027,12 +1040,12 @@ ability.stat.damagereduction = [stat]{0}%[lightgray] giảm sát thương ability.stat.minspeed = tốc độ tối thiểu [stat]{0} ô/giây[lightgray] ability.stat.duration = thời hạn [stat]{0} giây[lightgray] ability.stat.buildtime = thời gian xây [stat]{0} giây[lightgray] -bar.onlycoredeposit = Chỉ có thể đưa vào căn cứ +bar.onlycoredeposit = Chỉ có thể đưa vào lõi bar.drilltierreq = Cần máy khoan tốt hơn bar.noresources = Thiếu tài nguyên -bar.corereq = Yêu cầu căn cứ -bar.corefloor = Cần vùng thích hợp xây căn cứ +bar.corereq = Yêu cầu lõi +bar.corefloor = Cần vùng thích hợp xây lõi bar.cargounitcap = Đã đạt sức chứa khối hàng tối đa bar.drillspeed = Tốc độ khoan: {0}/giây bar.pumpspeed = Tốc độ bơm: {0}/giây @@ -1048,15 +1061,15 @@ bar.capacity = Sức chứa: {0} bar.unitcap = {0} {1}/{2} bar.liquid = Chất lỏng bar.heat = Nhiệt độ -bar.instability = Mức ổn định +bar.instability = Độ ổn định bar.heatamount = Lượng nhiệt: {0} bar.heatpercent = Lượng nhiệt: {0} ({1}%) bar.power = Năng lượng bar.progress = Đang xây dựng bar.loadprogress = Tiến trình -bar.launchcooldown = Chờ phóng +bar.launchcooldown = Hồi phóng bar.input = Đầu vào -bar.output = Sản phẩm +bar.output = Đầu ra bar.strength = [stat]{0}[lightgray]x Sức mạnh units.processorcontrol = [lightgray]Điều khiển bởi bộ xử lý @@ -1068,28 +1081,28 @@ bullet.homing = [stat]truy đuổi bullet.armorpierce = [stat]xuyên giáp bullet.maxdamagefraction = [stat]{0}%[lightgray] giới hạn sát thương bullet.suppression = [stat]{0} giây[lightgray] ngăn sửa chữa ~ [stat]{1}[lightgray] ô -bullet.interval = [stat]{0}/giây[lightgray] interval bullets: +bullet.interval = [stat]{0}/giây[lightgray] đạn ngắt quãng: bullet.frags = [stat]phá mảnh bullet.lightning = [stat]{0}[lightgray]x tia chớp ~ [stat]{1}[lightgray] sát thương -bullet.buildingdamage = [stat]{0}%[lightgray] sát thương khối -bullet.knockback = [stat]{0}[lightgray] bật lùi -bullet.pierce = [stat]{0}[lightgray]x xuyên mục tiêu +bullet.buildingdamage = [stat]{0}%[lightgray] sát thương công trình +bullet.knockback = [stat]{0}[lightgray] đẩy lùi +bullet.pierce = [stat]{0}[lightgray]x xuyên thấu bullet.infinitepierce = [stat]xuyên thấu bullet.healpercent = [stat]{0}[lightgray]% sửa chữa -bullet.healamount = [stat]{0}[lightgray] Sửa chữa trực tiếp +bullet.healamount = [stat]{0}[lightgray] sửa chữa trực tiếp bullet.multiplier = [stat]{0}[lightgray]x lượng đạn bullet.reload = [stat]{0}%[lightgray] tốc độ bắn -bullet.range = [stat]{0}[lightgray] Phạm vi +bullet.range = [stat]{0}[lightgray] ô phạm vi -unit.blocks = Khối -unit.blockssquared = Khối² +unit.blocks = khối +unit.blockssquared = khối² unit.powersecond = đơn vị năng lượng/giây unit.tilessecond = ô/giây unit.liquidsecond = đơn vị chất lỏng/giây unit.itemssecond = vật phẩm/giây unit.liquidunits = đơn vị chất lỏng unit.powerunits = đơn vị năng lượng -unit.heatunits = Đơn vị nhiệt +unit.heatunits = đơn vị nhiệt unit.degrees = độ unit.seconds = giây unit.minutes = phút @@ -1103,15 +1116,15 @@ unit.thousands = ng unit.millions = tr unit.billions = tỷ unit.pershot = /phát bắn -category.purpose = Mô tả +category.purpose = Mục đích category.general = Chung category.power = Năng lượng category.liquids = Chất lỏng category.items = Vật phẩm -category.crafting = Vào/Sản phẩm +category.crafting = Đầu vào/ra category.function = Chức năng -category.optional = Cải tiến -setting.skipcoreanimation.name = Bỏ qua hiệu ứng phóng/đáp căn cứ +category.optional = Cải tiến tùy chọn +setting.skipcoreanimation.name = Bỏ qua hiệu ứng phóng/đáp lõi setting.landscape.name = Khóa ngang setting.shadows.name = Bóng đổ setting.blockreplace.name = Tự động đề xuất khối @@ -1124,28 +1137,28 @@ setting.doubletapmine.name = Nhấn đúp để Đào setting.commandmodehold.name = Nhấn giữ để vào chế độ khiển đơn vị setting.distinctcontrolgroups.name = Giới hạn một nhóm điều khiển cho mỗi đơn vị setting.modcrashdisable.name = Tắt các mod khi gặp sự cố trong khởi động -setting.animatedwater.name = Hiệu ứng nước -setting.animatedshields.name = Hiệu ứng khiên -setting.playerindicators.name = Hướng người chơi -setting.indicators.name = Hướng kẻ địch +setting.animatedwater.name = Chuyển động bề mặt +setting.animatedshields.name = Chuyển động khiên +setting.playerindicators.name = Chỉ hướng người chơi +setting.indicators.name = Chỉ hướng kẻ địch setting.autotarget.name = Tự động nhắm mục tiêu setting.keyboard.name = Điều khiển bằng chuột + bàn phím setting.touchscreen.name = Điều khiển bằng màn hình cảm ứng setting.fpscap.name = FPS tối đa setting.fpscap.none = Không giới hạn setting.fpscap.text = {0} FPS -setting.uiscale.name = Kích thước giao diện +setting.uiscale.name = Tỉ lệ giao diện setting.uiscale.description = Trò chơi sẽ khởi động lại để áp dụng các thay đổi. setting.swapdiagonal.name = Đặt luôn theo đường chéo setting.difficulty.training = Luyện tập setting.difficulty.easy = Dễ setting.difficulty.normal = Vừa setting.difficulty.hard = Khó -setting.difficulty.insane = Rất khó +setting.difficulty.insane = Điên loạn setting.difficulty.name = Độ khó: setting.screenshake.name = Rung chuyển khung hình -setting.bloomintensity.name = Mức độ vụ nổ -setting.bloomblur.name = Xoá mờ vụ nổ +setting.bloomintensity.name = Mức độ phát sáng +setting.bloomblur.name = Xoá mờ phát sang setting.effects.name = Hiển thị hiệu ứng setting.destroyedblocks.name = Hiển thị khối bị phá setting.blockstatus.name = Hiển thị trạng thái khối @@ -1155,16 +1168,16 @@ setting.saveinterval.name = Khoảng thời gian lưu setting.seconds = {0} giây setting.milliseconds = {0} mili-giây setting.fullscreen.name = Toàn màn hình -setting.borderlesswindow.name = Không viền -setting.borderlesswindow.name.windows = Toàn màn hình (không viền) +setting.borderlesswindow.name = Cửa sổ không viền +setting.borderlesswindow.name.windows = Toàn màn hình không viền setting.borderlesswindow.description = Trò chơi có thể sẽ khởi động lại để áp dụng các thay đổi setting.fps.name = Hiển thị FPS & Ping setting.console.name = Bật Bảng điều khiển -setting.smoothcamera.name = Chế độ mượt mà +setting.smoothcamera.name = Khung quay mượt mà setting.vsync.name = Đồng bộ dọc (VSync) setting.pixelate.name = Đồ họa pixel -setting.minimap.name = Hiển thị bản đồ mini -setting.coreitems.name = Hiển thị vật phẩm trong căn cứ +setting.minimap.name = Hiển thị bản đồ nhỏ +setting.coreitems.name = Hiển thị vật phẩm trong lõi setting.position.name = Hiển thị vị trí người chơi setting.mouseposition.name = Hiện vị trí trỏ chuột setting.musicvol.name = Âm lượng nhạc @@ -1172,10 +1185,10 @@ setting.atmosphere.name = Hiển thị bầu khí quyển hành tinh setting.drawlight.name = Vẽ Bóng tối/Ánh sáng setting.ambientvol.name = Âm lượng tổng setting.mutemusic.name = Tắt nhạc -setting.sfxvol.name = Âm lượng SFX +setting.sfxvol.name = Âm lượng hiệu ứng âm thanh (SFX) setting.mutesound.name = Tắt tiếng -setting.crashreport.name = Gửi báo cáo sự cố -setting.savecreate.name = Tự động lưu +setting.crashreport.name = Gửi báo cáo sự cố ẩn danh +setting.savecreate.name = Tự động tạo bản lưu setting.steampublichost.name = Hiển thị trò chơi công khai setting.playerlimit.name = Giới hạn người chơi setting.chatopacity.name = Độ mờ trò chuyện @@ -1189,10 +1202,10 @@ setting.macnotch.description = Trò chơi sẽ khởi động lại để áp d steam.friendsonly = Chỉ bạn bè steam.friendsonly.tooltip = Liệu chỉ bạn bè trên Steam mới có thể tham gia trò chơi của bạn hay không.\nBỏ chọn ô này sẽ làm trò chơi của bạn công khai - mọi có thể tham gia. public.beta = Lưu ý rằng phiên bản beta của trò chơi không thể tạo sảnh công khai. -uiscale.reset = Kích thước giao diện đã được thay đổi.\nNhấn "OK" để xác nhận.\n[scarlet]Hoàn lại và thoát trong[accent] {0}[] giây... +uiscale.reset = Tỉ lệ giao diện đã được thay đổi.\nNhấn "Đồng ý" để xác nhận.\n[scarlet]Hoàn lại và thoát trong[accent] {0}[] giây... uiscale.cancel = Hủy & Thoát setting.bloom.name = Hiệu ứng phát sáng -keybind.title = Sửa phím +keybind.title = Gán lại phím keybinds.mobile = [scarlet]Hầu hết phím ở đây không hoạt động trên thiết bị di động. Chỉ hỗ trợ di chuyển cơ bản. category.general.name = Chung category.view.name = Xem @@ -1204,7 +1217,7 @@ keybind.respawn.name = Hồi sinh keybind.control.name = Điều khiển đơn vị keybind.clear_building.name = Xóa công trình keybind.press = Nhấn một phím... -keybind.press.axis = Nhấn một tổ hợp phím hoặc một phím... +keybind.press.axis = Nhấn một trục xoay hoặc một phím... keybind.screenshot.name = Chụp ảnh bản đồ keybind.toggle_power_lines.name = Ẩn/Hiện đường truyền năng lượng keybind.toggle_block_status.name = Ẩn/Hiện trạng thái khối @@ -1217,24 +1230,27 @@ keybind.command_mode.name = Chế độ mệnh lệnh keybind.command_queue.name = Lệnh tuần tự đơn vị keybind.create_control_group.name = Tạo nhóm điều khiển keybind.cancel_orders.name = Hủy lệnh + keybind.unit_stance_shoot.name = Tư thế đơn vị: Bắn keybind.unit_stance_hold_fire.name = Tư thế đơn vị: Ngừng bắn keybind.unit_stance_pursue_target.name = Tư thế đơn vị: Bám đuổi mục tiêu keybind.unit_stance_patrol.name = Tư thế đơn vị: Tuần tra keybind.unit_stance_ram.name = Tư thế đơn vị: Tông thẳng -keybind.unit_command_move.name = Unit Command: Move -keybind.unit_command_repair.name = Unit Command: Repair -keybind.unit_command_rebuild.name = Unit Command: Rebuild -keybind.unit_command_assist.name = Unit Command: Assist -keybind.unit_command_mine.name = Unit Command: Mine -keybind.unit_command_boost.name = Unit Command: Boost -keybind.unit_command_load_units.name = Unit Command: Load Units -keybind.unit_command_load_blocks.name = Unit Command: Load Blocks -keybind.unit_command_unload_payload.name = Unit Command: Unload Payload -keybind.unit_command_enter_payload.name = Unit Command: Enter Payload + +keybind.unit_command_move.name = Mệnh lệnh đơn vị: Di chuyển +keybind.unit_command_repair.name = Mệnh lệnh đơn vị: Sửa chữa +keybind.unit_command_rebuild.name = Mệnh lệnh đơn vị: Xây lại +keybind.unit_command_assist.name = Mệnh lệnh đơn vị: Hỗ trợ +keybind.unit_command_mine.name = Mệnh lệnh đơn vị: Khai thác +keybind.unit_command_boost.name = Mệnh lệnh đơn vị: Tăng cường +keybind.unit_command_load_units.name = Mệnh lệnh đơn vị: Nhập đơn vị +keybind.unit_command_load_blocks.name = Mệnh lệnh đơn vị: Nhập khối công trình +keybind.unit_command_unload_payload.name = Mệnh lệnh đơn vị: Dỡ khối hàng +keybind.unit_command_enter_payload.name = Mệnh lệnh đơn vị: Vào khối hàng + keybind.rebuild_select.name = Chọn khu vực xây dựng lại keybind.schematic_select.name = Chọn khu vực -keybind.schematic_menu.name = Menu bản thiết kế +keybind.schematic_menu.name = Trình đơn bản thiết kế keybind.schematic_flip_x.name = Lật bản thiết kế X keybind.schematic_flip_y.name = Lật bản thiết kế Y keybind.category_prev.name = Danh mục trước @@ -1253,7 +1269,7 @@ keybind.block_select_07.name = Danh mục/Khối 7 keybind.block_select_08.name = Danh mục/Khối 8 keybind.block_select_09.name = Danh mục/Khối 9 keybind.block_select_10.name = Danh mục/Khối 10 -keybind.fullscreen.name = Chế độ toàn màn hình +keybind.fullscreen.name = Hoán chuyển toàn màn hình keybind.select.name = Chọn/Bắn keybind.diagonal_placement.name = Đặt chéo keybind.pick.name = Chọn khối @@ -1265,10 +1281,10 @@ keybind.pickupCargo.name = Nhặt hàng keybind.dropCargo.name = Thả hàng keybind.shoot.name = Bắn keybind.zoom.name = Thu phóng -keybind.menu.name = Menu +keybind.menu.name = Trình đơn keybind.pause.name = Tạm dừng keybind.pause_building.name = Tạm dừng/Tiếp tục Xây -keybind.minimap.name = Bản đồ mini +keybind.minimap.name = Bản đồ nhỏ keybind.planet_map.name = Bản đồ hành tinh keybind.research.name = Nghiên cứu keybind.block_info.name = Thông tin khối @@ -1277,13 +1293,13 @@ keybind.player_list.name = Danh sách người chơi keybind.console.name = Bảng điều khiển keybind.rotate.name = Xoay keybind.rotateplaced.name = Xoay khối (Giữ) -keybind.toggle_menus.name = Ẩn/Hiện Menu +keybind.toggle_menus.name = Ẩn/Hiện Trình đơn keybind.chat_history_prev.name = Lịch sử trò chuyện trước keybind.chat_history_next.name = Lịch sử trò chuyện sau keybind.chat_scroll.name = Cuộn trò chuyện keybind.chat_mode.name = Thay đổi chế độ trò chuyện -keybind.drop_unit.name = Thả quân -keybind.zoom_minimap.name = Thu phóng bản đồ mini +keybind.drop_unit.name = Thả đơn vị +keybind.zoom_minimap.name = Thu phóng bản đồ nhỏ mode.help.title = Mô tả chế độ mode.survival.name = Sinh tồn mode.survival.description = Chế độ bình thường. Tài nguyên hạn chế và lượt đến tự động.\n[gray]Yêu cầu nơi xuất hiện kẻ địch trong bản đồ để chơi. @@ -1295,14 +1311,14 @@ mode.pvp.description = Chiến đấu với những người chơi khác trên c mode.attack.name = Tấn công mode.attack.description = Phá hủy căn cứ của kẻ địch. \n[gray]Cần căn cứ màu đỏ trong bản đồ để chơi. mode.custom = Tùy chỉnh luật + rules.invaliddata = Dữ liệu bộ nhớ tạm không hợp lệ. rules.hidebannedblocks = Ẩn các khối bị cấm - rules.infiniteresources = Tài nguyên vô hạn -rules.onlydepositcore = Chỉ cho phép đưa tài nguyên vào căn cứ +rules.onlydepositcore = Chỉ cho phép đưa tài nguyên vào lõi rules.derelictrepair = Cho phép sửa khối bỏ hoang rules.reactorexplosions = Nổ lò phản ứng -rules.coreincinerates = Hủy vật phẩm khi căn cứ đầy +rules.coreincinerates = Hủy vật phẩm khi lõi đầy rules.disableworldprocessors = Vô hiệu hoá bộ xử lý thế giới rules.schematic = Cho phép dùng bản thiết kế rules.wavetimer = Đếm ngược đợt @@ -1311,33 +1327,33 @@ rules.waves = Đợt rules.attack = Chế độ tấn công rules.buildai = AI Xây dựng căn cứ rules.buildaitier = Cấp độ AI xây dựng -rules.rtsai = AI Chiến thuật +rules.rtsai = AI Chiến thuật (WIP - Đang thực hiện) rules.rtsminsquadsize = Kích thước đội hình tối thiểu rules.rtsmaxsquadsize = Kích thước đội hình tối đa rules.rtsminattackweight = Sức tấn công tối thiểu rules.cleanupdeadteams = Xóa công trình của đội bị đánh bại (PvP) -rules.corecapture = Chiếm căn cứ khi phá hủy +rules.corecapture = Chiếm lõi khi phá hủy rules.polygoncoreprotection = Bảo vệ lõi kiểu đa giác. rules.placerangecheck = Kiểm tra phạm vi xây dựng -rules.enemyCheat = Tài nguyên vô hạn (kẻ địch) +rules.enemyCheat = Tài nguyên kẻ địch vô hạn rules.blockhealthmultiplier = Hệ số độ bền khối rules.blockdamagemultiplier = Hệ số sát thương của khối -rules.unitbuildspeedmultiplier = Hệ số tốc độ sản xuất lính +rules.unitbuildspeedmultiplier = Hệ số tốc độ sản xuất đơn vị rules.unitcostmultiplier = Hệ số yêu cầu tài nguyên sản xuất đơn vị rules.unithealthmultiplier = Hệ số độ bền của đơn vị rules.unitdamagemultiplier = Hệ số sát thương của đơn vị rules.unitcrashdamagemultiplier = Hệ số sát thương của đơn vị khi bị bắn rơi rules.solarmultiplier = Hệ số năng lượng mặt trời -rules.unitcapvariable = Căn cứ tăng giới hạn đơn vị +rules.unitcapvariable = Lõi tăng giới hạn đơn vị rules.unitpayloadsexplode = Khối hàng mang theo phát nổ cùng đơn vị -rules.unitcap = Giới hạn đơn vị +rules.unitcap = Giới hạn đơn vị ban đầu rules.limitarea = Giới hạn kích thước bản đồ -rules.enemycorebuildradius = Bán kính không xây dựng trong căn cứ của kẻ địch:[lightgray] (ô) +rules.enemycorebuildradius = Bán kính không xây dựng từ lõi của kẻ địch:[lightgray] (ô) rules.wavespacing = Thời gian giữa các đợt:[lightgray] (giây) -rules.initialwavespacing = Thời gian giữa các đợt ban đầu:[lightgray] (sec) +rules.initialwavespacing = Thời gian giữa các đợt ban đầu:[lightgray] (giây) rules.buildcostmultiplier = Hệ số chi phí xây dựng rules.buildspeedmultiplier = Hệ số tốc độ xây dựng -rules.deconstructrefundmultiplier = Hệ số số vật phẩm hoàn lại khi phá công trình +rules.deconstructrefundmultiplier = Hệ số số vật phẩm hoàn lại khi phá dỡ công trình rules.waitForWaveToEnd = Đợt chờ hết kẻ địch rules.wavelimit = Bản đồ kết thúc sau lượt rules.dropzoneradius = Bán kính vùng thả:[lightgray] (ô) @@ -1353,17 +1369,18 @@ rules.title.environment = Môi trường rules.title.teams = Đội rules.title.planet = Hành tinh rules.lighting = Ánh sáng -rules.fog = Hạn chế tầm nhìn +rules.fog = Sương mù chiến tranb rules.fire = Lửa -rules.anyenv = +rules.anyenv = rules.explosions = Sát thương nổ của Khối/Đơn vị rules.ambientlight = Ánh sáng môi trường rules.weather = Thời tiết rules.weather.frequency = Tần suất: rules.weather.always = Luôn luôn rules.weather.duration = Thời gian: -rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. -rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. + +rules.placerangecheck.info = Ngăn chặn người chơi khỏi việc đặt bất kỳ thứ gì gần công trình kẻ thù. Khi cố đặt một bệ súng, phạm vi sẽ bị tăng lên, để bệ súng không thể bắn tới kẻ địch. +rules.onlydepositcore.info = Ngăn chặn các đơn vị khỏi việc thả vật phẩm vào bất kỳ công trình nào ngoài lõi. content.item.name = Vật phẩm content.liquid.name = Chất lỏng @@ -1372,6 +1389,7 @@ content.block.name = Khối content.status.name = Trạng thái hiệu ứng content.sector.name = Khu vực content.team.name = Phe + wallore = (Tường) item.copper.name = Đồng @@ -1447,6 +1465,7 @@ unit.scepter.name = Scepter unit.reign.name = Reign unit.vela.name = Vela unit.corvus.name = Corvus + unit.stell.name = Stell unit.locus.name = Locus unit.precept.name = Precept @@ -1498,7 +1517,7 @@ block.scrap-wall.name = Tường phế liệu block.scrap-wall-large.name = Tường phế liệu lớn block.scrap-wall-huge.name = Tường phế liệu khổng lồ block.scrap-wall-gigantic.name = Tường phế liệu siêu khổng lồ -block.thruster.name = Thruster +block.thruster.name = Máy đẩy block.kiln.name = Lò nung block.graphite-press.name = Máy nén than chì block.multi-press.name = Máy nén than chì lớn @@ -1521,7 +1540,7 @@ block.snow.name = Tuyết block.crater-stone.name = Đá miệng núi lửa block.sand-water.name = Nước cát block.darksand-water.name = Nước cát đen -block.char.name = Char +block.char.name = Than block.dacite.name = Đá Dacit block.rhyolite.name = Đá Ryolit block.dacite-wall.name = Tường Dacit @@ -1620,9 +1639,9 @@ block.liquid-void.name = Hủy chất lỏng block.power-void.name = Hủy năng lượng block.power-source.name = Nguồn năng lượng block.unloader.name = Điểm dỡ hàng -block.vault.name = Nhà kho +block.vault.name = Kho chứa block.wave.name = Wave -block.tsunami.name = Sóng thần +block.tsunami.name = Tsunami block.swarmer.name = Swarmer block.salvo.name = Salvo block.ripple.name = Ripple @@ -1640,8 +1659,8 @@ block.pulse-conduit.name = Ống dẫn titan block.plated-conduit.name = Ống dẫn bọc giáp block.phase-conduit.name = Ống dẫn lượng tử block.liquid-router.name = Bộ phân phát chất lỏng -block.liquid-tank.name = Thùng chất lỏng -block.liquid-container.name = Bình chất lỏng +block.liquid-tank.name = Bể chứa chất lỏng +block.liquid-container.name = Thùng chứa chất lỏng block.liquid-junction.name = Giao điểm chất lỏng block.bridge-conduit.name = Cầu dẫn chất lỏng block.rotary-pump.name = Bơm điện @@ -1665,16 +1684,16 @@ block.rtg-generator.name = Máy phát điện RTG block.spectre.name = Spectre block.meltdown.name = Meltdown block.foreshadow.name = Foreshadow -block.container.name = Kho chứa +block.container.name = Thùng chứa block.launch-pad.name = Bệ phóng block.segment.name = Segment block.ground-factory.name = Nhà máy Bộ binh block.air-factory.name = Nhà máy Không quân block.naval-factory.name = Nhà máy Hải quân -block.additive-reconstructor.name = Máy nâng cấp đơn vị cấp 2 -block.multiplicative-reconstructor.name = Máy nâng cấp đơn vị cấp 3 -block.exponential-reconstructor.name = Máy nâng cấp đơn vị cấp 4 -block.tetrative-reconstructor.name = Máy nâng cấp đơn vị cấp 5 +block.additive-reconstructor.name = Máy tái thiết cấp cộng +block.multiplicative-reconstructor.name = Máy tái thiết cấp nhân +block.exponential-reconstructor.name = Máy tái thiết cấp mũ +block.tetrative-reconstructor.name = Máy tái thiết cấp lũy thừa lặp block.payload-conveyor.name = Băng chuyền khối hàng block.payload-router.name = Bộ định tuyến khối hàng block.duct.name = Ống chân không @@ -1699,6 +1718,8 @@ block.payload-unloader.name = Máy dỡ vật phẩm block.payload-unloader.description = Lấy chất lỏng và vật phẩm từ khối. block.heat-source.name = Nguồn nhiệt block.heat-source.description = Khối này cho bạn vô hạn nhiệt. + +#Erekir block.empty.name = Trống block.rhyolite-crater.name = Miệng Núi Lửa Rhyolit block.rough-rhyolite.name = Rhyolite Thô @@ -1722,8 +1743,8 @@ block.arkyic-vent.name = Lỗ Thông Hơi Arkyic block.yellow-stone-vent.name = Lỗ Thông Hơi Đá Vàng block.red-stone-vent.name = Lỗ Thông Hơi Đá Đỏ block.crystalline-vent.name = Lỗ thông hơi Pha Lê -block.redmat.name = Redmat -block.bluemat.name = Bluemat +block.redmat.name = Thảm đỏ +block.bluemat.name = Thảm xanh block.core-zone.name = Khu Vực Căn Cứ block.regolith-wall.name = Tường Regolith block.yellow-stone-wall.name = Tường Đá Vàng @@ -1737,8 +1758,8 @@ block.red-ice-wall.name = Tường Băng Đỏ block.red-stone-wall.name = Tường Đá Đỏ block.red-diamond-wall.name = Tường Kim Cương Đỏ block.redweed.name = Rêu Đỏ -block.pur-bush.name = Pur Bush -block.yellowcoral.name = Yellowcoral +block.pur-bush.name = Bụi cây tím +block.yellowcoral.name = San hô vàng block.carbon-boulder.name = Tảng Đá Carbon block.ferric-boulder.name = Tảng Đá Ferric block.beryllic-boulder.name = Tảng Đá Beryllic @@ -1798,7 +1819,7 @@ block.reinforced-conduit.name = Ống dẫn cứng block.reinforced-liquid-junction.name = Điểm giao ống dẫn cứng block.reinforced-bridge-conduit.name = Cầu ống dẫn cứng block.reinforced-liquid-router.name = Bộ phân phát chất lỏng cứng -block.reinforced-liquid-container.name = Container chất lỏng gia cố +block.reinforced-liquid-container.name = Thùng chứa chất lỏng gia cố block.reinforced-liquid-tank.name = Bể chứa chất lỏng gia cố block.beam-node.name = Chốt tia điện block.beam-tower.name = Chốt tia điện lớn @@ -1815,8 +1836,8 @@ block.eruption-drill.name = Máy khoan siêu động lực block.core-bastion.name = Căn cứ: Pháo đài block.core-citadel.name = Căn cứ: Thủ phủ block.core-acropolis.name = Căn cứ: Đại đô -block.reinforced-container.name = Container gia cố -block.reinforced-vault.name = Kho gia cố +block.reinforced-container.name = Thùng chứa gia cố +block.reinforced-vault.name = Kho chứa gia cố block.breach.name = Breach block.sublimate.name = Sublimate block.titan.name = Titan @@ -1846,8 +1867,9 @@ block.diffuse.name = Diffuse block.basic-assembler-module.name = Module lắp ráp đơn vị block.smite.name = Smite block.malign.name = Malign -block.flux-reactor.name = Lò phản ứng bốc hơi +block.flux-reactor.name = Lò phản ứng thông lượng block.neoplasia-reactor.name = Lò phản ứng siêu tân sinh + block.switch.name = Công tắc block.micro-processor.name = Bộ xử lý nhỏ block.logic-processor.name = Bộ xử lý @@ -1868,14 +1890,14 @@ hint.skip = Bỏ qua hint.desktopMove = Sử dụng [accent][[WASD][] để di chuyển. hint.zoom = [accent]Cuộn[] để phóng to hoặc thu nhỏ. hint.desktopShoot = [accent][[chuột trái][] để bắn. -hint.depositItems = Để di chuyển các vật phẩm, hãy kéo từ tàu của bạn đến căn cứ. +hint.depositItems = Để di chuyển các vật phẩm, hãy kéo từ tàu của bạn đến lõi. hint.respawn = Để hồi sinh như tàu của bạn, nhấn [accent][[V][]. hint.respawn.mobile = Bạn đã chuyển điều khiển một đơn vị/công trình. Để hồi sinh như một con tàu, [accent]nhấn vào hình đại diện ở phía trên cùng bên trái.[] hint.desktopPause = Nhấn [accent][[Space][] để tạm dừng và tiếp tục trò chơi. hint.breaking = [accent]Chuột phải[] và kéo để phá vỡ các khối. hint.breaking.mobile = Kích hoạt \ue817 [accent]Cây búa[] ở phía dưới cùng bên phải và nhấn để phá vỡ các khối.\n\nGiữ ngón tay của bạn trong một giây và kéo để phá khối trong vùng được chọn. hint.blockInfo = Xem thông tin của một khối bằng cách chọn nó trong [accent]menu xây dựng[], Sau đó chọn nút [accent][[?][] ở bên phải. -hint.derelict = [accent]Bỏ hoang[] là các công trình bị hỏng của các căn cứ cũ mà không còn hoạt động.\n\nCác công trình này có thể [accent]được tháo dỡ[] để nhận được nguyên liệu. +hint.derelict = [accent]Bỏ hoang[] là các công trình bị hỏng của các căn cứ cũ mà không còn hoạt động.\n\nCác công trình này có thể [accent]được tháo dỡ[] để nhận được tài nguyên hoặc sửa chữa. hint.research = Sử dụng nút \ue875 [accent]Nghiên cứu[] để nghiên cứu công nghệ mới. hint.research.mobile = Sử dụng nút \ue875 [accent]Nghiên cứu[] trong \ue88c [accent]Trình đơn[] để nghiên cứu công nghệ mới. hint.unitControl = Giữ [accent][[L-ctrl][] và [accent]nhấp chuột[] để điều khiển đơn vị của bạn hoặc súng. @@ -1883,8 +1905,8 @@ hint.unitControl.mobile = [accent]Nhấp đúp[] để điều khiển đơn v hint.unitSelectControl = Để điều khiển đơn vị, hãy nhấn [accent]chế độ ra lệnh[] bằng cách giữ [accent]L-shift.[]\nKhi ở chế độ lệnh, hãy nhấn và kéo để chọn đơn vị. [accent]Nhấn chuột phải[] vào một vị trí hoặc mục tiêu để ra lệnh đơn vị đó. hint.unitSelectControl.mobile = Để điều khiển đơn vị, hãy nhấn [accent]chế độ ra lệnh[] bằng cách nhấn nút [accent]lệnh[] ở phía dưới cùng bên trái.\nKhi ở chế độ ra lệnh, hãy nhấn và kéo để chọn đơn vị. Tap vào một vị trí hoặc mục tiêu để ra lệnh đơn vị đó. hint.launch = Sau khi thu thập đủ tài nguyên, bạn có thể [accent]Phóng[] bằng cách chọn các khu vực lân cận từ \ue827 [accent]Bản đồ[] ở phía dưới cùng bên phải. -hint.launch.mobile = Sau khi thu thập đủ tài nguyên, bạn có thể [accent]Phóng[] bằng cách chọn các khu vực lân cận từ \ue827 [accent]Bản đồ[] trong \ue88c [accent]Menu[]. -hint.schematicSelect = Giữ [accent][[F][] và kéo để chọn các khối để sao chép và dán.\n\n[accent][[Middle Click][] để sao chép một khối. +hint.launch.mobile = Sau khi thu thập đủ tài nguyên, bạn có thể [accent]Phóng[] bằng cách chọn các khu vực lân cận từ \ue827 [accent]Bản đồ[] trong \ue88c [accent]Trình đơn[]. +hint.schematicSelect = Giữ [accent][[F][] và kéo để chọn các khối để sao chép và dán.\n\n[accent][[Nhấp chuột giữa][] để sao chép một khối. hint.rebuildSelect = Giữ [accent][[B][] và kéo để chọn các khối bị hỏng.\nChúng sẽ được tự động được xây lại. hint.rebuildSelect.mobile = Chọn nút \ue874 sao chép, sau đó nhấp nút \ue80f xây lại và kéo để chọn khu vực của các khối đã bị phá hủy.\nViệc này sẽ giúp xây lại chúng một cách tự động. hint.conveyorPathfind = Giữ [accent][[L-Ctrl][] trong khi kéo băng chuyền để tự động tạo đường dẫn. @@ -1901,16 +1923,17 @@ hint.coreUpgrade = Các căn cứ có thể được nâng cấp bằng cách [a hint.presetLaunch = Khác khu vực đáp [accent] xám[], như [accent]Frozen Forest[], có thể được phóng đến từ bất cứ đâu. Nó không yêu cầu chiếm các khu vực lân cận.\n\n[accent]Các khu vực được đánh số[], chẳng hạn như cái này, là [accent]không bắt buộc[]. hint.presetDifficulty = Khu vực này có [scarlet]mối đe dọa cao[].\nPhóng đến khu vực như vậy [accent]không được khuyến khích[] nếu không có công nghệ và chuẩn bị phù hợp. hint.coreIncinerate = Sau khi căn cứ đầy vật phẩm, bất kì vật phẩm vào thuộc loại đó nhận được sẽ bị [accent]tiêu hủy[]. -hint.factoryControl = Để đặt [accent]hướng đầu ra[] của một nhà máy, nhấp vào một khối nhà máy trong chế độ ra lệnh, sau đó nhấp chuột phải vào một vị trí.\nCác đơn vị sản xuất bởi nó sẽ tự động di chuyển đến đó. -hint.factoryControl.mobile = Để đặt [accent]hướng đầu ra[] của một nhà máy, tap vào một khối nhà máy trong chế độ ra lệnh, sau đó tap vào một vị trí.\nCác đơn vị sản xuất bởi nó sẽ tự động di chuyển đến đó. -gz.mine = Di chuyển gần \uf8c4 [accent]quặng đồng[] trên đất và click vào nó để bắt đầu khai thác. +hint.factoryControl = Để đặt [accent]điểm đầu ra[] của một nhà máy, nhấp vào một khối nhà máy trong chế độ ra lệnh, sau đó nhấp chuột phải vào một vị trí.\nCác đơn vị sản xuất bởi nó sẽ tự động di chuyển đến đó. +hint.factoryControl.mobile = Để đặt [accent]điểm đầu ra[] của một nhà máy, nhấp vào một khối nhà máy trong chế độ ra lệnh, sau đó nhấp vào một vị trí.\nCác đơn vị sản xuất bởi nó sẽ tự động di chuyển đến đó. + +gz.mine = Di chuyển gần \uf8c4 [accent]quặng đồng[] trên đất và nhấp vào nó để bắt đầu khai thác. gz.mine.mobile = Di chuyển gần \uf8c4 [accent]quặng đồng[] trên đất và nhấp vào nó để bắt đầu khai thác. -gz.research = Mở \ue875 tiến trình.\nNghiên cứu \uf870 [accent]Máy khoan cơ khí[], sau đó chọn nó từ menu ở góc dưới bên phải.\nNhấp vào một quặng đồng để đặt nó. -gz.research.mobile = Mở \ue875 tiến trình.\nNghiên cứu \uf870 [accent]Máy khoan cơ khí[], sau đó chọn nó từ menu ở góc dưới bên phải.\nNhấp vào một quặng đồng để đặt nó.\n\nNhấp vào \ue800 [accent]dấu tích[] ở góc dưới bên phải để xác nhận. +gz.research = Mở \ue875 tiến trình.\nNghiên cứu \uf870 [accent]Máy khoan cơ khí[], sau đó chọn nó từ trình đơn ở góc dưới bên phải.\nNhấp vào một quặng đồng để đặt nó. +gz.research.mobile = Mở \ue875 tiến trình.\nNghiên cứu \uf870 [accent]Máy khoan cơ khí[], sau đó chọn nó từ trình đơn ở góc dưới bên phải.\nNhấp vào một quặng đồng để đặt nó.\n\nNhấp vào \ue800 [accent]dấu tích[] ở góc dưới bên phải để xác nhận. gz.conveyors = Nghiên cứu và đặt \uf896 [accent]băng chuyền[] để di chuyển các nguyên liệu được khai thác\n từ các máy khoan đến căn cứ.\n\nNhấp và kéo để đặt nhiều băng chuyền.\n[accent]Cuộn[] để xoay. gz.conveyors.mobile = Nghiên cứu và đặt \uf896 [accent]băng chuyền[] để di chuyển các nguyên liệu được khai thác\n từ các máy khoan đến căn cứ.\n\nGiữ ngón tay một chút và kéo để đặt nhiều băng chuyền. gz.drills = Mở rộng hoạt động khai thác.\nĐặt thêm Máy khoan cơ khí.\nKhai thác 100 đồng. -gz.lead = \uf837 [accent]Chì[] là một nguyên liệu được sử dụng phổ biến.\nHãy đặt các máy khoan để khai thác chì. +gz.lead = \uf837 [accent]Chì[] là một tài nguyên được sử dụng phổ biến.\nHãy đặt các máy khoan để khai thác chì. gz.moveup = \ue804 Di chuyển lên để xem các nhiệm vụ tiếp theo. gz.turrets = Nghiên cứu và đặt 2 súng \uf861 [accent]Duo[] để bảo vệ căn cứ. Súng Duo cần \uf838 [accent]đạn[] từ băng chuyền. gz.duoammo = Tiếp đạn cho súng Duo bằng [accent]đồng[], sử dụng băng chuyền. @@ -1918,18 +1941,19 @@ gz.walls = [accent]Tường[] có thể ngăn chặn sát thương đến các c gz.defend = Quân địch đang đến, chuẩn bị bảo vệ. gz.aa = Các đơn vị bay không thể dễ dàng bị bắn hạ với các súng tiêu chuẩn.\n\uf860 [accent]Scatter[] cung cấp tốt khả năng phòng không, nhưng cần \uf837 [accent]chì[] là đạn. gz.scatterammo = Tiếp đạn cho súng Scatter bằng [accent]chì[], sử dụng băng chuyền. -gz.supplyturret = [accent]Súng cung cấp +gz.supplyturret = [accent]Cấp đạn cho súng gz.zone1 = Đây là khu vực quân địch đáp xuống. gz.zone2 = Bất kỳ thứ gì được xây dựng trong bán kính này sẽ bị phá hủy khi một đợt mới bắt đầu. gz.zone3 = Đợt tiếp theo sẽ bắt đầu ngay bây giờ.\nHãy chuẩn bị. gz.finish = Đặt thêm các súng, khai thác thêm nguyên liệu,\nvà vừa bảo vệ căn cứ, vượt qua tất cả các đợt để [accent]chiếm khu vực[]. + onset.mine = Nhấp để khai thác \uf748 [accent]beryllium[] từ tường.\n\nSử dụng [accent][[WASD] để di chuyển. onset.mine.mobile = Nhấp để khai thác \uf748 [accent]beryllium[] từ tường. onset.research = Mở \ue875 tiến trình.\nNghiên cứu, sau đó đặt \uf73e [accent]Turbine điện hơi nước[] trên lỗ thông hơi.\nĐiều này sẽ tạo ra [accent]điện[]. onset.bore = Nghiên cứu và đặt \uf741 [accent]Khoan plasma[].\nĐiều này sẽ tự động khai thác nguyên liệu từ tường. onset.power = Để nối [accent]điện[] cho khoan plasma, nghiên cứu và đặt \uf73d [accent]Chốt tia điện[].\nKết nối Turbine điện hơi nước với khoan plasma. -onset.ducts = Nghiên cứu và đặt \uf799 [accent]ống chân không[] để di chuyển các nguyên liệu được khai thác từ các máy khoan đến căn cứ.\nNhấp và kéo để đặt nhiều ống chân không.\n[accent]Cuộn[] để xoay. -onset.ducts.mobile = Nghiên cứu và đặt \uf799 [accent]ống chân không[] để di chuyển các nguyên liệu được khai thác từ các máy khoan đến căn cứ.\n\nGiữ ngón tay một chút và kéo để đặt nhiều ống chân không. +onset.ducts = Nghiên cứu và đặt \uf799 [accent]ống chân không[] để di chuyển các nguyên liệu được khai thác từ các máy khoan đến lõi.\nNhấp và kéo để đặt nhiều ống chân không.\n[accent]Cuộn[] để xoay. +onset.ducts.mobile = Nghiên cứu và đặt \uf799 [accent]ống chân không[] để di chuyển các nguyên liệu được khai thác từ các máy khoan đến lõi.\n\nGiữ ngón tay một chút và kéo để đặt nhiều ống chân không. onset.moremine = Mở rộng hoạt động khai thác.\nĐặt thêm Máy khoan plasma và sử dụng chốt tia điện và ống chân không để kết nối và vận chuyển.\nKhai thác 200 beryllium. onset.graphite = Các khối phức tạp hơn cần \uf835 [accent]than chì[].\nĐặt khoan plasma để khai thác than chì. onset.research2 = Bắt đầu nghiên cứu [accent]nhà máy[].\nNghiên cứu \uf74d [accent]máy phá đá[] và \uf779 [accent]lò tinh luyện silicon[]. @@ -1938,21 +1962,22 @@ onset.crusher = Sử dụng \uf74d [accent]máy phá đá[] để khai thác cá onset.fabricator = Sử dụng [accent]đơn vị[] để khám phá bản đồ, bảo vệ các công trình và tấn công quân địch.\nNghiên cứu và đặt \uf6a2 [accent]máy chế tạo xe tăng[]. onset.makeunit = Sản xuất một đơn vị.\nSử dụng nút "?" để xem các yêu cầu của máy đã chọn. onset.turrets = Các đơn vị rất tốt, nhưng [accent]súng[] cung cấp khả năng phòng thủ tốt hơn nếu được sử dụng hiệu quả.\nĐặt một \uf6eb [accent]Breach[].\nSúng cần \uf748 [accent]đạn[]. -onset.turretammo = Tiếp đạn cho súng bằng [accent]beryllium[]. +onset.turretammo = Tiếp đạn cho súng bằng [accent]beryllium[] dùng ống chân không. onset.walls = [accent]Tường[] có thể ngăn chặn sát thương đến các công trình.\nĐặt một số \uf6ee [accent]tường beryllium[] xung quanh súng. onset.enemies = Quân địch đang đến, hãy chuẩn bị phòng thủ. onset.defenses = [accent]Thiết lập phòng thủ:[lightgray] {0} onset.attack = Quân địch đã suy yếu.\nHãy phản công. -onset.cores = Các căn cứ có thể được đặt trên [accent]ô căn cứ[].\nCác căn cứ mới có thể được đặt ở bất kỳ đâu trên bản đồ.\nĐặt một \uf725 căn cứ. +onset.cores = Các lõi mới có thể được đặt trên [accent]ô đặt lõi[].\nCác lõi mới hoạt động như một tiền cứ và chia sẻ kho tài nguyên với các lõi khác.\nĐặt một \uf725 lõi. onset.detect = Quân địch sẽ phát hiện bạn trong vòng 2 phút.\nHãy chuẩn bị phòng thủ, khai thác và sản xuất. -onset.commandmode = Giữ [accent]Shift[] để vào [accent]chế độ điều khiển quân[].\n[accent]Nhấp chuột trái và kéo[] để chọn các đơn vị.\n[accent]Chuộc phải[] để điều khiển các đơn vị di chuyển hoặc tấn công. -onset.commandmode.mobile = Nhấn vào [accent]nút điều khiển[] để vào [accent]chế độ điều khiển quân[].\nGiữ một ngón tay, sau đó [accent]kéo[] để chọn các đơn vị.\n[accent]Nhấp[] để điều khiển các đơn vị di chuyển hoặc tấn công. -aegis.tungsten = Tungsten có thể khai thác bằng [accent]khoan thủy lực[].\nCông trình này cần có [accent]nước[] và [accent]năng lượng[]. -split.pickup = Một số khối có thể được mang bởi đơn vị.\nNhấp vào [accent]container[] và đặt nó lên [accent]máy nạp vật phẩm[].\n(Phím mặc định là [ và ] để mang và thả) -split.pickup.mobile = Một số khối có thể được mang bởi đơn vị.\nNhấp vào [accent]container[] và đặt nó lên [accent]máy nạp vật phẩm[].\n(Để mang hoặc thả một khối, ấn giữ nó một chút.) +onset.commandmode = Giữ [accent]Shift[] để vào [accent]chế độ điều khiển đơn vị[].\n[accent]Nhấp chuột trái và kéo[] để chọn các đơn vị.\n[accent]Chuột phải[] để điều khiển các đơn vị di chuyển hoặc tấn công. +onset.commandmode.mobile = Nhấn vào [accent]nút mệnh lệnh[] để vào [accent]chế độ điều khiển đơn vị[].\nGiữ một ngón tay, sau đó [accent]kéo[] để chọn các đơn vị.\n[accent]Nhấp[] để điều khiển các đơn vị di chuyển hoặc tấn công. +aegis.tungsten = Tungsten có thể khai thác bằng [accent]khoan thủy lực[].\nCông trình này cần có [accent]nước[] và [accent]điện[]. + +split.pickup = Một số khối có thể được mang theo bởi đơn vị.\nNhấp vào [accent]thùng chứa[] và đặt nó lên [accent]máy nạp vật phẩm[].\n(Phím mặc định là [ và ] để mang theo và thả) +split.pickup.mobile = Một số khối có thể được mang theo bởi đơn vị.\nNhấp vào [accent]thùng chứa[] và đặt nó lên [accent]máy nạp vật phẩm[].\n(Để mang theo hoặc thả một khối, ấn giữ nó một chút.) split.acquire = Bạn cần một số tungsten để sản xuất đơn vị. split.build = Đơn vị phải được vận chuyển đến phía bên kia của tường.\nĐặt hai [accent]Máy phóng từ trường[], một ở mỗi bên của tường.\nĐặt liên kết bằng cách nhấp vào một trong số chúng, sau đó chọn cái còn lại. -split.container = Tương tự như container, đơn vị cũng có thể được vận chuyển bằng [accent]Máy phóng từ trường[].\nĐặt một máy chế tạo đơn vị cạnh máy phóng từ trường để nạp chúng, sau đó gửi chúng qua tường để tấn công căn cứ địch. +split.container = Tương tự như thùng chứa, đơn vị cũng có thể được vận chuyển bằng [accent]Máy phóng từ trường[].\nĐặt một máy chế tạo đơn vị cạnh máy phóng từ trường để nạp chúng, sau đó gửi chúng qua tường để tấn công căn cứ địch. item.copper.description = Dùng trong tất cả các khu xây dựng và các loại đạn dược. item.copper.details = Đồng, là kim loại phổ biến trên Serpulo. Có cấu trúc yếu trừ khi được tôi luyện. @@ -1966,38 +1991,42 @@ item.coal.details = Có vẻ là vật chất hóa thạch của thực vật, h item.titanium.description = Dùng trong cấu trúc vận chuyển chất lỏng, máy khoan và các công trình. item.thorium.description = Dùng trong các công trình bền vững và có thể dùng làm nhiên liệu hạt nhân. item.scrap.description = Dùng làm nguyên liệu cho Máy nung phế liệu và Máy nghiền để tinh luyện thành các vật liệu khác. -item.scrap.details = Tàn tích còn lại của các công trình và robot cũ. -item.silicon.description = Dùng trong các tấm pin mặt trời, thiết bị điện tử phức tạp và một số loại đạn dược. -item.plastanium.description = Dùng trong các robot tiên tiến, các cấu trúc cách điện và đạn tự phân mảnh. +item.scrap.details = Tàn tích còn lại của các công trình và đơn vị cũ. +item.silicon.description = Dùng trong các tấm pin mặt trời, thiết bị điện tử phức tạp và một số loại đạn dược truy đuổi. +item.plastanium.description = Dùng trong các đơn vị tiên tiến, các cấu trúc cách điện và đạn tự phân mảnh. item.phase-fabric.description = Dùng trong các thiết bị điện tử tiên tiến và các cấu trúc tự sửa chữa. item.surge-alloy.description = Dùng trong các vũ khí tiên tiến và các cấu trúc phòng thủ phản ứng. item.spore-pod.description = Dùng để chuyển đổi thành dầu, chất nổ và nhiên liệu. item.spore-pod.details = Bào tử. Có thể là một dạng sống tổng hợp. Phát thải khí độc đối với sinh vật khác. Cực kỳ xâm lấn. Rất dễ cháy trong một số trường hợp nhất định. item.blast-compound.description = Dùng trong bom hoặc đạn nổ. item.pyratite.description = Dùng trong vũ khí gây cháy và máy phát điện chạy bằng nhiên liệu đốt. + +#Erekir item.beryllium.description = Được sử dụng trong nhiều loại công trình và đạn dược trên Erekir. item.tungsten.description = Được sử dụng trong các máy khoan, áo giáp và đạn dược. Yêu cầu trong việc xây dựng các công trình cao cấp hơn. item.oxide.description = Dùng làm chất dẫn nhiệt và cách điện cho nguồn điện. -item.carbide.description = Được sử dụng trong các công trình tiên tiến, các đơn vị mạnh và đạn dược. +item.carbide.description = Được sử dụng trong các công trình tiên tiến, các đơn vị hạng nặng và đạn dược. liquid.water.description = Dùng để làm mát máy móc và xử lý chất thải. -liquid.slag.description = Dùng để tách các kim loại, hoặc phun vào kẻ thù như một loại vũ khí. +liquid.slag.description = Dùng để tách các kim loại. Dùng trong các loại súng chất lỏng nhưng một loại đạn. liquid.oil.description = Dùng trong sản xuất vật liệu tiên tiến và làm đạn gây cháy. liquid.cryofluid.description = Dùng làm chất làm mát trong lò phản ứng, súng và nhà máy. + +#Erekir liquid.arkycite.description = Được sử dụng trong các phản ứng hóa học để phát điện và tổng hợp vật liệu. liquid.ozone.description = Được sử dụng như một chất oxy hóa trong sản xuất vật liệu và làm nhiên liệu. Gây nổ vừa phải. liquid.hydrogen.description = Được sử dụng trong khai thác tài nguyên, sản xuất đơn vị và sửa chữa công trình. Dễ cháy. liquid.cyanogen.description = Được sử dụng cho đạn dược, xây dựng các đơn vị tiên tiến và các phản ứng khác nhau trong các khối tiên tiến. Rất dễ cháy. liquid.nitrogen.description = Được sử dụng trong khai thác tài nguyên, tạo khí và sản xuất đơn vị. Trơ. liquid.neoplasm.description = Một sản phẩm phụ sinh học nguy hiểm của lò phản ứng Neoplasia. Lan nhanh sang bất kì khối chứa nước nào mà nó chạm vào và gây hư hại chúng. Nhớt. -liquid.neoplasm.details = Neoplasm. Một khối lượng các tế bào tổng hợp phân chia nhanh chóng không kiểm soát với độ đặc giống như bùn. Kháng nhiệt. Cực kì nguy hiểm cho bất cứ khối nào có liên quan đến nước.\n\nQuá phức tạp và không ổn định để được phân tích. Chưa rõ được tiềm năng và ứng dụng của nó. Khuyến nghị đốt chúng trong xỉ nóng chảy +liquid.neoplasm.details = Tế bào tân sinh. Một khối lượng các tế bào tổng hợp phân chia nhanh chóng không kiểm soát với độ đặc giống như bùn. Kháng nhiệt. Cực kì nguy hiểm cho bất cứ khối nào có liên quan đến nước.\n\nQuá phức tạp và không ổn định để được phân tích. Chưa rõ được tiềm năng và ứng dụng của nó. Khuyến nghị đốt chúng trong xỉ nóng chảy. block.derelict = \uf77e [lightgray]Bỏ hoang -block.armored-conveyor.description = Vận chuyển vật phẩm về phía trước. Không nhận đầu vào từ phía bên cạnh. +block.armored-conveyor.description = Vận chuyển vật phẩm về phía trước. Không nhận đầu vào không phải băng chuyền từ phía bên cạnh. block.illuminator.description = Phát sáng. block.message.description = Lưu trữ tin nhắn giao tiếp giữa đồng đội. -block.reinforced-message.description = Lưu trữ một thông điệp để liên lạc giữa các đồng minh. -block.world-message.description = Một khối thông điệp đùng để sử dụng trong việc làm bản đồ. Không thể bị phá hủy. +block.reinforced-message.description = Lưu trữ tin nhắn giao tiếp giữa đồng đội. +block.world-message.description = Một khối tin nhắn đùng để sử dụng trong việc tạo bản đồ. Không thể bị phá hủy. block.graphite-press.description = Nén than thành than chì. block.multi-press.description = Nén than thành than chì. Cần nước làm mát. block.silicon-smelter.description = Tinh chế silicon từ cát và than. @@ -2011,14 +2040,14 @@ block.pyratite-mixer.description = Trộn than, chì và cát thành nhiệt th block.melter.description = Nung phế liệu thành xỉ. block.separator.description = Tách xỉ thành các thành phần khoáng của nó. block.spore-press.description = Nén vỏ bào tử thành dầu. -block.pulverizer.description = Nghiền phế liệu thành cát. -block.coal-centrifuge.description = Biến dầu thành than. +block.pulverizer.description = Nghiền phế liệu thành cát mịn. +block.coal-centrifuge.description = Biến đổi dầu thành than. block.incinerator.description = Tiêu hủy bất kỳ vật phẩm hoặc chất lỏng nào mà nó nhận được. block.power-void.description = Hủy tất cả năng lượng nhận được. Chỉ có trong chế độ tự do. -block.power-source.description = Tạo ra năng lượng mãi mãi. Chỉ có trong chế độ tự do. -block.item-source.description = Tạo ra vật phẩm mãi mãi. Chỉ có trong chế độ tự do. +block.power-source.description = Tạo ra năng lượng vô hạn. Chỉ có trong chế độ tự do. +block.item-source.description = Tạo ra vật phẩm vô hạn. Chỉ có trong chế độ tự do. block.item-void.description = Hủy mọi vật phẩm. Chỉ có trong chế độ tự do. -block.liquid-source.description = Tạo ra chất lỏng mãi mãi. Chỉ có trong chế độ tự do. +block.liquid-source.description = Tạo ra chất lỏng vô hạn. Chỉ có trong chế độ tự do. block.liquid-void.description = Loại bỏ mọi chất lỏng. Chỉ có trong chế độ tự do. block.payload-source.description = Tạo ra bất kì khối hàng nào. Chỉ có trong chế độ tự do. block.payload-void.description = Phá hủy bất kì khối hàng nào. Chỉ có trong chế độ tự do. @@ -2032,19 +2061,19 @@ block.thorium-wall.description = Bảo vệ công trình khỏi đạn của k block.thorium-wall-large.description = Bảo vệ nhiều công trình khỏi đạn của kẻ thù. block.phase-wall.description = Bảo vệ công trình khỏi đạn của kẻ thù, phản hầu hết đạn khi va chạm. block.phase-wall-large.description = Bảo vệ nhiều công trình khỏi đạn của kẻ thù, phản hầu hết đạn khi va chạm. -block.surge-wall.description = Bảo vệ công trình khỏi đạn của kẻ thù, đôi khi tạo ra tia điện khi bị bắn. -block.surge-wall-large.description = Bảo vệ nhiều công trình khỏi đạn của kẻ thù, đôi khi tạo ra tia điện khi bị bắn. +block.surge-wall.description = Bảo vệ công trình khỏi đạn của kẻ thù, đôi khi tạo ra tia điện khi đạn va chạm. +block.surge-wall-large.description = Bảo vệ nhiều công trình khỏi đạn của kẻ thù, đôi khi tạo ra tia điện khi đạn va chạm. block.door.description = Một bức tường có thể đóng mở. block.door-large.description = Một bức tường có thể đóng mở. block.mender.description = Sửa chữa định kỳ các khối trong vùng lân cận.\nSử dụng Sợi lượng tử để tăng phạm vi và hiệu quả. block.mend-projector.description = Sửa chữa các khối lân cận.\nSử dụng Sợi lượng tử để tăng phạm vi và hiệu quả. -block.overdrive-projector.description = Tăng tốc độ làm việc của các công trình lân cận.\nSử dụng Sợi lượng tử để tăng phạm vi và hiệu quả. -block.force-projector.description = Tạo ra một trường lực lục giác xung quanh nó, bảo vệ các công trình và đơn vị bên trong khỏi bị hư hại.\nQuá nóng nếu chịu quá nhiều sát thương. Sử dụng chất làm mát để giảm nhiệt độ. Sử dụng Sợi lượng tử để tăng kích thước lá chắn. +block.overdrive-projector.description = Tăng tốc độ làm việc của các công trình gần đó.\nSử dụng Sợi lượng tử để tăng phạm vi và hiệu quả. +block.force-projector.description = Tạo ra một trường lực lục giác xung quanh nó, bảo vệ các công trình và đơn vị bên trong khỏi bị hư hại.\nQuá nóng nếu chịu quá nhiều sát thương. Tùy chọn sử dụng chất làm mát để chống quá nhiệt. Sử dụng Sợi lượng tử để tăng kích thước lá chắn. block.shock-mine.description = Giải phóng tia điện khi tiếp xúc với đơn vị đối phương. block.conveyor.description = Vận chuyển vật phẩm về phía trước. block.titanium-conveyor.description = Vận chuyển vật phẩm về phía trước. Nhanh hơn băng chuyền tiêu chuẩn. block.plastanium-conveyor.description = Vận chuyển vật phẩm về phía trước theo lô. Nhận các vật phẩm ở phía sau và dỡ chúng theo ba hướng ở phía trước. Yêu cầu nhiều điểm tải và dỡ hàng để có hiệu quả cao nhất. -block.junction.description = Hoạt động như một cầu nối cho hai băng chuyền băng qua. +block.junction.description = Hoạt động như một cầu nối cho hai băng chuyền băng qua nhau. block.bridge-conveyor.description = Vận chuyển vật phẩm qua nhiều loại địa hình hoặc công trình. block.phase-conveyor.description = Vận chuyển tức thời vật phẩm qua địa hình hoặc công trình. Phạm vi dài hơn cầu nối, nhưng cần năng lượng. block.sorter.description = Nếu vật phẩm giống vật phẩm được chọn sẽ được chuyển đến trước, nếu không sẽ được chuyển qua trái hoặc phải. @@ -2064,7 +2093,7 @@ block.plated-conduit.description = Đẩy chất lỏng đến trước, không block.liquid-router.description = Nhận chất lỏng từ một phía và đưa đều ra ba phía còn lại. Có thể trữ một lượng chất lỏng nhất định. block.liquid-container.description = Lưu trữ một lượng vừa phải chất lỏng. Đưa đều ra mọi phía như bộ phân phát chất lỏng. block.liquid-tank.description = Trữ được một lượng lớn chất lỏng. Đưa đều ra mọi phía như bộ phân phát chất lỏng. -block.liquid-junction.description = Chức năng như cầu cho hai ống nước băng chéo nhau. +block.liquid-junction.description = Chức năng như cầu cho hai ống nước băng qua nhau. block.bridge-conduit.description = Vận chuyển chất lỏng qua nhiều loại địa hình hoặc công trình. block.phase-conduit.description = Vận chuyển chất lỏng qua địa hình hoặc công trình. Phạm vi dài hơn cầu nối, nhưng cần năng lượng. block.power-node.description = Truyền năng lượng cho các chốt được kết nối. Chốt sẽ nhận năng lượng hoặc cấp năng lượng cho bất kỳ khối nào liền kề. @@ -2082,76 +2111,78 @@ block.solar-panel.description = Cung cấp một lượng nhỏ năng lượng t block.solar-panel-large.description = Cung cấp một lượng nhỏ năng lượng từ mặt trời. Hiệu quả hơn pin mặt trời tiêu chuẩn. block.thorium-reactor.description = Tạo ra lượng năng lượng lớn từ thorium. Yêu cầu làm mát liên tục. Sẽ phát nổ dữ dội nếu không cung cấp đủ chất làm mát. block.impact-reactor.description = Tạo ra lượng năng lượng khổng lồ khi đạt hiệu quả cao nhất. Yêu cầu nguồn năng lượng lớn để bắt đầu quá trình. -block.mechanical-drill.description = Khi đặt trên quặng, xuất các vật phẩm với tốc độ chậm. Chỉ có khả năng khai thác các tài nguyên cơ bản. +block.mechanical-drill.description = Khi đặt trên quặng, xuất vô hạn các vật phẩm với tốc độ chậm. Chỉ có khả năng khai thác các tài nguyên cơ bản. block.pneumatic-drill.description = Máy khoan cải tiến, có khả năng khai thác titan. Khai thác với tốc độ nhanh hơn máy khoan cơ khí. block.laser-drill.description = Cho phép khoan nhanh hơn nhờ công nghệ laser, nhưng đòi hỏi năng lượng. Có khả năng khai thác thorium. block.blast-drill.description = Máy khoan siêu cấp. Yêu cầu lượng năng lượng lớn. -block.water-extractor.description = Khai thác nước ngầm. Được sử dụng ở những nơi không có sẵn nước. +block.water-extractor.description = Khai thác nước ngầm. Được sử dụng ở những nơi bề mặt không có sẵn nước. block.cultivator.description = Lọc bào tử có trong không khí và nuôi cấy thành vỏ bào tử. block.cultivator.details = Công nghệ được phục hồi. Được sử dụng để sản xuất một lượng lớn bào tử. Có thể là nơi ủ ban đầu của các bào tử hiện đang bao phủ Serpulo. block.oil-extractor.description = Sử dụng lượng năng lượng năng lớn, sử dụng cát và nước để khoan dầu. block.core-shard.description = Trung tâm của căn cứ. Sau khi bị phá hủy, khu vực này sẽ bị mất. -block.core-shard.details = Căn cứ cấp 1. Gọn nhẹ. Tự thay thế. Được trang bị tên lửa đẩy dùng một lần. Không được thiết kế để di chuyển giữa các hành tinh. -block.core-foundation.description = Trung tâm của căn cứ. Được bọc giáp. Chứa được nhiều tài nguyên hơn Căn cứ: Cơ sỏ. -block.core-foundation.details = Căn cứ cấp 2. +block.core-shard.details = Lõi cấp 1. Gọn nhẹ. Tự thay thế. Được trang bị tên lửa đẩy dùng một lần. Không được thiết kế để di chuyển giữa các hành tinh. +block.core-foundation.description = Trung tâm của căn cứ. Được bọc giáp. Chứa được nhiều tài nguyên hơn Căn cứ: Cơ sở. +block.core-foundation.details = Lõi cấp 2. block.core-nucleus.description = Lõi của căn cứ. Bọc giáp chắc chắn. Lưu trữ lượng lớn tài nguyên. -block.core-nucleus.details = Căn cứ cấp 3 và cũng là cấp cao nhất. +block.core-nucleus.details = Lõi cấp 3 và cũng là cấp cao nhất. block.vault.description = Lưu trữ lượng lớn vật phẩm mỗi loại. Nội dung có thể được lấy ra với điểm dỡ hàng. -block.container.description = Lưu trữ lượng lớn vật phẩm mỗi loại. Nội dung có thể được lấy ra với điểm dỡ hàng. +block.container.description = Lưu trữ lượng nhỏ vật phẩm mỗi loại. Mở rộng kho lưu trữ khi đặt kế bên một lõi. Nội dung có thể được lấy ra với điểm dỡ hàng. block.unloader.description = Lấy các vật phẩm được chọn từ các ô gần đó. -block.launch-pad.description = Phóng lô vật phẩm vào phân vùng. +block.launch-pad.description = Phóng lô vật phẩm vào khu vực. block.launch-pad.details = Hệ thống quỹ đạo phụ để vận chuyển tài nguyên từ điểm này sang điểm khác. Các nhóm tải trọng rất dễ vỡ và không có khả năng tồn tại khi tái nhập. block.duo.description = Bắn xen kẽ đạn vào kẻ địch. -block.scatter.description = Bắn chì, phế liệu hoặc thuỷ tinh vào kẻ địch trên không. -block.scorch.description = Đốt mọi kẻ địch trên mặt đất. Hiệu quả cao ở tầm gần. -block.hail.description = Phóng đạn nhỏ vào kẻ địch trên mặt đất ở tầm xa. -block.wave.description = Phóng một tia chất lỏng vào kẻ địch. Tự chữa cháy nếu được cung cấp nước. -block.lancer.description = Tích tụ và phóng tia năng lượng mạnh vào kẻ địch trên mặt đất. -block.arc.description = Phóng tia điện vào kẻ địch trên mặt đất. +block.scatter.description = Bắn khối chì, phế liệu hoặc thuỷ tinh vào kẻ địch trên không. +block.scorch.description = Đốt mọi kẻ địch trên mặt đất ở gần. Hiệu quả cao ở tầm gần. +block.hail.description = Bắn đạn nhỏ vào kẻ địch trên mặt đất ở tầm xa. +block.wave.description = Bắn một tia chất lỏng vào kẻ địch. Tự chữa cháy nếu được cung cấp nước. +block.lancer.description = Tích tụ và bắn tia năng lượng mạnh vào kẻ địch trên mặt đất. +block.arc.description = Bắn tia điện vào kẻ địch trên mặt đất. block.swarmer.description = Bắn tên lửa truy đuổi vào kẻ địch. block.salvo.description = Bắn loạt đạn vào kẻ địch. -block.fuse.description = Bắn ba đạn xuyên giáp tầm gần vào kẻ địch. +block.fuse.description = Bắn ba tia đạn xuyên giáp tầm gần vào kẻ địch. block.ripple.description = Bắn cụm đạn vào kẻ địch trên mặt đất ở tầm xa. -block.cyclone.description = Bắn đạn nổ vào kẻ địch ở gần. -block.spectre.description = Bắn đạn xuyên giáp lớn ở kẻ địch trên không và trên mặt đất. -block.meltdown.description = Nạp và bắn một tia laser liên tục vào kẻ địch ở gần. Cần có chất làm mát để hoạt động. +block.cyclone.description = Bắn cụm đạn nổ vào kẻ địch ở gần. +block.spectre.description = Bắn đạn lớn ở kẻ địch trên không và trên mặt đất. +block.meltdown.description = Tích tụ và bắn một tia laser liên tục vào kẻ địch ở gần. Cần có chất làm mát để hoạt động. block.foreshadow.description = Bắn viên một viên đạn tỉa lớn ở tầm xa. Ưu tiên kẻ địch có độ bền tối đa cao nhất. -block.repair-point.description = Liên tục sửa chữa robot ở trong phạm vi hoạt động. +block.repair-point.description = Liên tục sửa chữa đơn vị ở trong phạm vi hoạt động. block.segment.description = Gây hư hại và phá hủy đạn đến. Ngoại trừ tia laser. -block.parallax.description = Bắn một tia kéo máy bay địch và làm hư hỏng nó trong quá trình kéo. +block.parallax.description = Bắn một tia kéo mục tiêu trên không và làm hư hỏng nó trong quá trình kéo. block.tsunami.description = Phóng một tia chất lỏng mạnh vào kẻ địch. Tự chữa cháy nếu được cung cấp nước hoặc chất làm mát. block.silicon-crucible.description = Tinh chế silicon từ cát và than, sử dụng nhiệt thạch làm nguồn nhiệt phụ. Có hiệu quả cao hơn khi ở nơi nóng. block.disassembler.description = Tách xỉ thành các kim loại khác nhau với hiệu suất thấp. Có thể sản xuất thorium. block.overdrive-dome.description = Tăng tốc độ làm việc của các công trình lân cận. Sử dụng sợi lượng tử and silicon để hoạt động. -block.payload-conveyor.description = Di chuyển những khối hàng lớn, chẳng hạn như các đơn vị từ nhà máy. +block.payload-conveyor.description = Di chuyển những khối hàng lớn, chẳng hạn như các đơn vị từ nhà máy. Từ tính. Sử dụng ở những môi trường không trọng lực. block.payload-router.description = Tách những khối hàng đầu vào thành 3 hướng đầu ra. -block.ground-factory.description = Sản xuất binh lính bộ binh. Các đơn vị đầu ra có thể được sử dụng trực tiếp, hoặc đem nâng cấp. -block.air-factory.description = Sản xuất binh lính không quân. Các đơn vị đầu ra có thể được sử dụng trực tiếp, hoặc đem nâng cấp. -block.naval-factory.description = Sản xuất binh lính hải quân. Các đơn vị đầu ra có thể được sử dụng trực tiếp, hoặc đem nâng cấp. +block.ground-factory.description = Sản xuất đơn vị bộ binh. Các đơn vị đầu ra có thể được sử dụng trực tiếp, hoặc đưa vào máy tái thiết để nâng cấp. +block.air-factory.description = Sản xuất đơn vị không quân. Các đơn vị đầu ra có thể được sử dụng trực tiếp, hoặc đưa vào máy tái thiết để nâng cấp. +block.naval-factory.description = Sản xuất đơn vị hải quân. Các đơn vị đầu ra có thể được sử dụng trực tiếp, hoặc đưa vào máy tái thiết để nâng cấp. block.additive-reconstructor.description = Nâng cấp quân của bạn lên cấp hai. block.multiplicative-reconstructor.description = Nâng cấp quân của bạn lên cấp ba. block.exponential-reconstructor.description = Nâng cấp quân của bạn lên cấp bốn. -block.tetrative-reconstructor.description = Nâng cấp quân của bạn lên cấp năm (cuối cùng). -block.switch.description = Công tắc, trạng thái có thể được đọc và điều khiển với xử lý logic. -block.micro-processor.description = Chạy tập hợp các chỉ dẫn trong một vòng lặp, có thể dùng để điều khiển robot và công trình. -block.logic-processor.description = Chạy tập hợp các chỉ dẫn trong một vòng lặp, có thể dùng để điều khiển robot và công trình. Nhanh hơn bộ xử lý nhỏ. -block.hyper-processor.description = Chạy tập hợp các chỉ dẫn trong một vòng lặp, có thể dùng để điều khiển robot và công trình. Nhanh hơn bộ xử lý. +block.tetrative-reconstructor.description = Nâng cấp quân của bạn lên cấp năm và là cấp cuối cùng. +block.switch.description = Công tắc bật/tắt, trạng thái có thể được đọc và điều khiển với xử lý logic. +block.micro-processor.description = Chạy tập hợp các chỉ lệnh trong một vòng lặp, có thể dùng để điều khiển đơn vị và công trình. +block.logic-processor.description = Chạy tập hợp các chỉ lệnh trong một vòng lặp, có thể dùng để điều khiển đơn vị và công trình. Nhanh hơn bộ xử lý nhỏ. +block.hyper-processor.description = Chạy tập hợp các chỉ lệnh trong một vòng lặp, có thể dùng để điều khiển đơn vị và công trình. Nhanh hơn bộ xử lý. block.memory-cell.description = Lưu trữ thông tin cho bộ xử lý. block.memory-bank.description = Lưu trữ thông tin cho bộ xử lý. Dung lượng cao. block.logic-display.description = Hiển thị đồ họa tùy ý từ bộ xử lý. block.large-logic-display.description = Hiển thị đồ họa tùy ý từ bộ xử lý. block.interplanetary-accelerator.description = Tòa súng từ trường cỡ lớn. Tăng tốc vật phóng đến vận tốc thoát để di chuyển giữa các hành tinh. block.repair-turret.description = Sửa chữa những đơn vị bị hư hỏng trong khu vực nhất định. Có thể làm mát để tăng hiệu quả. + +#Erekir block.core-bastion.description = Trung tâm của căn cứ. Bọc giáp. Khu vực sẽ mất khi bị phá hủy. block.core-citadel.description = Trung tâm của căn cứ. Bọc giáp tốt hơn. Lưu trữ nhiều vật phẩm hơn căn cứ Pháo đài. block.core-acropolis.description = Trung tâm của căn cứ. Được bọc giáp rất tốt. Lưu trữ nhiều vật phẩm hơn căn cứ Thủ Phủ. -block.breach.description = Bắn đạn beryllium hoặc tungsten gây cháy vào kẻ địch. -block.diffuse.description = Bắn một loạt đạn gây cháy theo hình nón. Đồng thời đẩy kẻ địch về phía sau. -block.sublimate.description = Thổi tia lửa vào kẻ thù. Có khả năng xuyên giáp. +block.breach.description = Bắn đạn beryllium hoặc tungsten xuyên thấu vào kẻ địch. +block.diffuse.description = Bắn một loạt đạn mảnh theo hình nón. Đồng thời đẩy kẻ địch về phía sau. +block.sublimate.description = Thổi tia lửa vào kẻ thù. Xuyên giáp. block.titan.description = Bắn đạn pháo khổng lồ vào các mục tiêu trên mặt đất. Yêu cầu hydrogen. -block.afflict.description = Bắn ra các mảnh vỡ của một quả cầu tích điện khổng lồ. Yêu cầu được làm nóng. +block.afflict.description = Bắn ra các mảnh vỡ của một quả cầu tích điện khổng lồ. Yêu cầu nhiệt. block.disperse.description = Bắng các mảnh gây nổ vào các mục tiêu trên không. -block.lustre.description = Bắn một tia laser vào các mục tiêu của địch. +block.lustre.description = Bắn một tia laser di chuyển chậm một mục tiêu vào các mục tiêu của địch. block.scathe.description = Phóng một tên lửa mạnh vào các mục tiêu trên mặt đất ở khoảng cách lớn. block.smite.description = Bắn viên đạn phát sáng nổ xuyên thấu. block.malign.description = Bắn chùm hàng loạt những tia laser dẫn đường nhắm thẳng mục tiêu kẻ địch. Yêu cầu lượng nhệt lớn. @@ -2162,7 +2193,7 @@ block.slag-heater.description = Làm nóng khối đối diện. Yêu cầu xỉ block.phase-heater.description = Làm nóng khối đối diện. Yêu cầu sợi lượng tử. block.heat-redirector.description = Chuyển lượng nhiệt nhận được sang các khối khác. block.heat-router.description = Chuyển lượng nhiệt nhận được sang ba hướng còn lại. -block.electrolyzer.description = Chuyển đổi nước thành hydrogen và ozone. +block.electrolyzer.description = Chuyển đổi nước thành hydrogen và ozone. Các khi hóa lỏng được xuất hai hướng đối nhau, được đánh dấu bằng các màu tương ứng. block.atmospheric-concentrator.description = Cô đặc nitơ từ khí quyển. Yêu cầu nhiệt. block.surge-crucible.description = Tinh chế hợp kim từ xỉ và silicon. Yêu cầu nhiệt. block.phase-synthesizer.description = Tổng hợp sợi lượng tử từ thorium, cát, và ozone. Yêu cầu nhiệt. @@ -2170,12 +2201,12 @@ block.carbide-crucible.description = Kết hợp than chì và tungsten để t block.cyanogen-synthesizer.description = Tổng hợp cyanogen từ arkycite và than chì. Yêu cầu nhiệt. block.slag-incinerator.description = Đốt các vật phẩm hoặc chất lỏng không bay hơi. Yêu cầu xỉ. block.vent-condenser.description = Ngưng tụ khí từ lỗ thông hơi để tạo ra nước. Tiêu thụ điện. -block.plasma-bore.description = Khi được đặt đối diện với một bức tường quặng, sản xuất vô hạn vật phẩm. Yêu cầu một lượng điện nhỏ. -block.large-plasma-bore.description = Một máy khoan plasma lớn hơn. Có thể khoan tungsten và thorium. Yêu cầu hydrogen và điện. +block.plasma-bore.description = Khi được đặt đối diện với một bức tường quặng, sản xuất vô hạn vật phẩm. Yêu cầu một lượng điện nhỏ.\nTùy chọn sử dụng hydrogen để tăng hiệu suất. +block.large-plasma-bore.description = Một máy khoan plasma lớn hơn. Có thể khoan tungsten và thorium. Yêu cầu hydrogen và điện.\nTùy chọn sử dụng nitrogen để tăng hiệu suất. block.cliff-crusher.description = Nghiền vách đá, xuất ra cát vô hạn. Yêu cầu năng lượng. Hiệu quả thay đổi dựa theo loại vách đá. block.impact-drill.description = Khi được đặt lên một loại quặng, sản xuất vô hạn vật phẩm. Yêu cầu điện và nước. block.eruption-drill.description = Phiên bản cải tiến củ máy khoan động lực. Có thể khoan thorium. Yêu cầu hydrogen. -block.reinforced-conduit.description = Di chuyển chất lỏng về phía trước. Không nhận đầu vào từ các bên. +block.reinforced-conduit.description = Di chuyển chất lỏng về phía trước. Không nhận đầu vào nếu không phải ống dẫn từ các bên. block.reinforced-liquid-router.description = Phân chia chất lỏng đều cho tất cả các bên. block.reinforced-liquid-tank.description = Lưu trữ một lượng chất lỏng lớn. block.reinforced-liquid-container.description = Lưu trữ một lượng chất lỏng vừa phải. @@ -2187,16 +2218,16 @@ block.tungsten-wall.description = Bảo vệ các công trình khỏi đạn c block.tungsten-wall-large.description = Bảo vệ các công trình khỏi đạn của kẻ thù. block.carbide-wall.description = Bảo vệ các công trình khỏi đạn của kẻ thù. block.carbide-wall-large.description = Bảo vệ các công trình khỏi đạn của kẻ thù. -block.reinforced-surge-wall.description = Bảo vệ các công trình khỏi đạn của kẻ thù, phóng ra các tia điện khi gặp đạn đối phương. -block.reinforced-surge-wall-large.description = Bảo vệ các công trình khỏi đạn của kẻ thù, phóng ra các tia điện khi gặp đạn đối phương.. -block.shielded-wall.description = Bảo vệ các công trình khỏi đạn của kẻ thù. Triển khai một cái khiên chống đạn khi được cung cấp điện. Dẫn điện. -block.blast-door.description = Một loại tường tự động mở ra khi đơn vị đang ở gần. Không thể điều khiển bằng tay. +block.reinforced-surge-wall.description = Bảo vệ các công trình khỏi đạn của kẻ thù, phóng ra các tia điện khi đạn va chạm. +block.reinforced-surge-wall-large.description = Bảo vệ các công trình khỏi đạn của kẻ thù, phóng ra các tia điện khi đạn va chạm. +block.shielded-wall.description = Bảo vệ các công trình khỏi đạn của kẻ thù. Phản hầu hết các loại đạn khi va chạm. Triển khai một lá chắn chống đạn khi được cung cấp điện. Dẫn điện. +block.blast-door.description = Một loại tường tự động mở ra khi đơn vị mặt đất đang ở gần. Không thể điều khiển một cách thủ công. block.duct.description = Di chuyển vật phẩm về phía trước. Chỉ có thể lưu trữ một vật phẩm. -block.armored-duct.description = Di chuyển vật phẩm về phía trước. Không nhận đầu vào từ các bên. +block.armored-duct.description = Di chuyển vật phẩm về phía trước. Không nhận đầu vào nếu không phải ống chân không từ các bên. block.duct-router.description = Phân chia vật phẩm đều cho tất cả các bên. Chỉ nhận đầu vào từ phía sau. Có thể được cấu hình thành một bộ sắp xếp vật phẩm. block.overflow-duct.description = Chỉ xuất vật phẩm ra các bên nếu đường dẫn phía trước bị chặn. block.duct-bridge.description = Vận chuyển vật phẩm qua các công trình và địa hình. -block.duct-unloader.description = Lấy ra vật phẩm đã chọn từ khối phía sau nó. Không thể lấy ra từ các căn cứ. +block.duct-unloader.description = Lấy ra vật phẩm đã chọn từ khối phía sau nó. Không thể lấy ra từ các lõi. block.underflow-duct.description = Ngược lại với một ống tràn chân không. Xuất ra phía trước nếu các đường dẫn bên trái và phải bị chặn. block.reinforced-liquid-junction.description = Làm cầu nối cho hai ống dẫn chất lỏng giao nhau. block.surge-conveyor.description = Di chuyển vật phẩm theo lô. Có thể được tăng tốc bằng điện. Dẫn điện. @@ -2209,21 +2240,21 @@ block.turbine-condenser.description = Tạo ra điện khi được đặt trên block.chemical-combustion-chamber.description = Tạo ra điện từ arkycite và ozone. block.pyrolysis-generator.description = Tạo ra một lượng điện lớn từ arkycite và xỉ nóng chảy. Tạo ra nước như một sản phẩm phụ. block.flux-reactor.description = Tạo ra một lượng điện lớn khi được làm nóng. Yêu cầu sử dụng cyanogen như một chất làm ổn định. Lượng điện tạo ra và lượng tiêu thụ cyanogen tỷ lệ thuận với lượng nhiệt nhận được.\nPhát nổ nếu không cung cấp đủ cyanogen. -block.neoplasia-reactor.description = Sử dụng arkycite, nước và sợi lượng tử để tạo ra lượng điện khổng lồ. Tạo ra nhiệt và neoplasm như sản phẩm phụ trong quá trình hoạt động.\nPhát nổ dữ dội nếu không được loại bỏ khỏi lò phản ứng. +block.neoplasia-reactor.description = Sử dụng arkycite, nước và sợi lượng tử để tạo ra lượng điện khổng lồ. Tạo ra nhiệt và lượng tế bào tân sinh nguy hiểm như sản phẩm phụ trong quá trình hoạt động.\nPhát nổ dữ dội nếu tế bào tân sinh không được loại bỏ khỏi lò phản ứng. block.build-tower.description = Tự động xây dựng lại các công trình trong phạm vi và hỗ trợ các đơn vị khác trong quá trình xây dựng. -block.regen-projector.description = Sửa chữa các công trình một cách chậm rãi trong phạm vi hình vuông. Yêu cầu hidrogen. -block.reinforced-container.description = Lưu trữ một lượng nhỏ vật phẩm. Vật phẩm có thể được lấy ra thông qua các điểm dỡ hàng. Không làm tăng khả năng lưu trữ của căn cứ. -block.reinforced-vault.description = Lưu trữ một lượng lớn vật phẩm. Vật phẩm có thể được lấy ra thông qua các điểm dỡ hàng. Không làm tăng khả năng lưu trữ của căn cứ. -block.tank-fabricator.description = Chế tạo đơn vị Stell. Các đơn vị được chế tạo có thể được sử dụng trực tiếp hoặc được chuyển vào các máy chế tạo khác để được nâng cấp. -block.ship-fabricator.description = Chế tạo đơn vị Elude. Các đơn vị được chế tạo có thể được sử dụng trực tiếp hoặc được chuyển vào các máy chế tạo khác để được nâng cấp. -block.mech-fabricator.description = Chế tạo đơn vị Merui. Các đơn vị được chế tạo có thể được sử dụng trực tiếp hoặc được chuyển vào các máy chế tạo khác để được nâng cấp. +block.regen-projector.description = Sửa chữa các công trình một cách chậm rãi trong phạm vi hình vuông. Yêu cầu hydrogen.\nTùy chọn sử dụng sợi lượng tử để tăng hiệu suất. +block.reinforced-container.description = Lưu trữ một lượng nhỏ vật phẩm. Vật phẩm có thể được lấy ra thông qua các điểm dỡ hàng. Không làm tăng khả năng lưu trữ của lõi. +block.reinforced-vault.description = Lưu trữ một lượng lớn vật phẩm. Vật phẩm có thể được lấy ra thông qua các điểm dỡ hàng. Không làm tăng khả năng lưu trữ của lõi. +block.tank-fabricator.description = Chế tạo đơn vị Stell. Các đơn vị được chế tạo có thể được sử dụng trực tiếp hoặc được chuyển vào các máy tái chế tạo khác để được nâng cấp. +block.ship-fabricator.description = Chế tạo đơn vị Elude. Các đơn vị được chế tạo có thể được sử dụng trực tiếp hoặc được chuyển vào các máy tái chế tạo khác để được nâng cấp. +block.mech-fabricator.description = Chế tạo đơn vị Merui. Các đơn vị được chế tạo có thể được sử dụng trực tiếp hoặc được chuyển vào các máy tái chế tạo khác để được nâng cấp. block.tank-assembler.description = Lắp ráp các xe tăng lớn từ các khối và đơn vị. Cấp độ đầu ra có thể được tăng bằng cách thêm các module. block.ship-assembler.description = Lắp ráp các phi thuyền lớn từ các khối và đơn vị. Cấp độ đầu ra có thể được tăng bằng cách thêm các module. -block.mech-assembler.description = Lắp ráp các lính cơ động lớn từ các khối và đơn vị. Cấp độ đầu ra có thể được tăng bằng cách thêm các module. -block.tank-refabricator.description = Nâng cấp các xe tăng lên cấp hai. -block.ship-refabricator.description = Nâng cấp các phi thuyền lên cấp hai. -block.mech-refabricator.description = Nâng cấp các đơn vị cơ động lên cấp hai. -block.prime-refabricator.description = Nâng cấp các đơn vị lên cấp ba. +block.mech-assembler.description = Lắp ráp các đơn vị cơ động lớn từ các khối và đơn vị. Cấp độ đầu ra có thể được tăng bằng cách thêm các module. +block.tank-refabricator.description = Nâng cấp các xe tăng đầu vào lên cấp hai. +block.ship-refabricator.description = Nâng cấp các phi thuyền đầu vào lên cấp hai. +block.mech-refabricator.description = Nâng cấp các đơn vị cơ động đầu vào lên cấp hai. +block.prime-refabricator.description = Nâng cấp các đơn vị đầu vào lên cấp ba. block.basic-assembler-module.description = Tăng cấp lắp ráp khi đặt cạnh một hệ thống lắp ráp. Yêu cầu điện. Có thể sử dụng như nơi nhập các khối hàng. block.small-deconstructor.description = Tháo dỡ các công trình và đơn vị đầu vào. Trả lại 100% chi phí tạo ra. block.reinforced-payload-conveyor.description = Di chuyển khối hàng tiến về phía trước. @@ -2238,41 +2269,43 @@ block.canvas.description = Hiển thị một hình ảnh đơn giản với m unit.dagger.description = Bắn đạn tiêu chuẩn vào tất cả kẻ địch xung quanh. unit.mace.description = Phun lửa vào tất cả kẻ địch xung quanh. unit.fortress.description = Bắn pháo tầm xa lên kẻ địch trên mặt đất. -unit.scepter.description = Bắn một chùm đạn vào kẻ địch ở gần. -unit.reign.description = Bắn một chùm đạn xuyên giáp vào kẻ địch ở gần. -unit.nova.description = Bắn tia laser làm tổn hại kẻ địch và sửa chữa các tòa nhà. Có khả năng bay. -unit.pulsar.description = Bắn tia điện làm tổn hại kẻ địch và sửa chữa các tòa nhà. Có khả năng bay. -unit.quasar.description = Bắn tia laser xuyên giáp làm tổn hại kẻ địch và sửa chữa các tòa nhà. Có khả năng bay. Được bọc giáp. -unit.vela.description = Bắn tia laser liên tục xuyên giáp làm tổn hại kẻ địch, gây cháy và sửa chữa các tòa nhà. Có khả năng bay. -unit.corvus.description = Bắn đia laser đánh bật kẻ địch và sửa chữa các tòa nhà. Có thể đi qua đa số địa hình. -unit.crawler.description = Chạy đến kẻ địch và nổ. -unit.atrax.description = Phun xỉ nóng chảy vào kẻ địch trên mặt đất. Có thể đi qua đa số địa hình. -unit.spiroct.description = Bắn tia laser vào kẻ địch trên mặt đất và tự sửa chữa nó. Có thể đi qua đa số địa hình. -unit.arkyid.description = Bắn tia laser lớn vào kẻ địch trên mặt đất và tự sửa chữa chính nó. Có thể đi qua đa số địa hình. -unit.toxopid.description = Bắn chùm đạn điện và tia laser xuyên giáp vào kẻ địch trên mặt đất và tự sửa chữa chính nó. Có thể đi qua đa số địa hình. -unit.flare.description = Bắn đạn thường vào kẻ địch tầm gần trên mặt đất. +unit.scepter.description = Bắn một chùm đạn tích tụ vào kẻ địch ở gần. +unit.reign.description = Bắn một chùm đạn xuyên giáp mạnh vào kẻ địch ở gần. +unit.nova.description = Bắn tia laser gây sát thương kẻ địch và sửa chữa các công trình đồng minh. Có khả năng bay. +unit.pulsar.description = Bắn tia điện gây sát thương kẻ địch và sửa chữa các công trình đồng minh. Có khả năng bay. +unit.quasar.description = Bắn tia laser xuyên giáp gây sát thương kẻ địch và sửa chữa các công trình đồng minh. Có khả năng bay. Có lá chắn. +unit.vela.description = Bắn tia laser liên tục xuyên giáp gây sát thương kẻ địch, gây cháy và sửa chữa các công trình đồng minh. Có khả năng bay. +unit.corvus.description = Bắn đia laser cực mạnh gây sát thương kẻ địch và sửa chữa các công trình đồng minh. Có thể đi qua đa số địa hình. +unit.crawler.description = Chạy thẳng đến kẻ địch và tự hủy, gây ra vụ nổ lớn. +unit.atrax.description = Phun tia xỉ nóng chảy gây suy nhược vào kẻ địch trên mặt đất. Có thể đi qua đa số địa hình. +unit.spiroct.description = Bắn tia laser lớn gây ăn mòn vào kẻ địch trên mặt đất và tự sửa chữa nó. Có thể đi qua đa số địa hình. +unit.arkyid.description = Bắn tia laser lớn gây ăn mòn vào kẻ địch trên mặt đất và tự sửa chữa chính nó. Có thể đi qua đa số địa hình. +unit.toxopid.description = Bắn chùm đạn điện và tia laser xuyên giáp vào kẻ địch trên mặt đất. Có thể đi qua đa số địa hình. +unit.flare.description = Bắn đạn tiêu chuẩn vào kẻ địch trên mặt đất. unit.horizon.description = Thả chùm bom lên kẻ địch trên mặt đất. -unit.zenith.description = Bắn chùm tên lửa vào mọi kẻ địch tầm gần. -unit.antumbra.description = Bắn chùm đạn vào mọi kẻ địch tầm gần. -unit.eclipse.description = Bắn hai tia laser xuyên giáp và chùm flak vào mọi kẻ địch tầm gần. -unit.mono.description = Tự động khai thác đồng và chì, và vận chuyển vào căn cứ. +unit.zenith.description = Bắn chùm tên lửa vào mọi kẻ địch. +unit.antumbra.description = Bắn chùm đạn vào mọi kẻ địch. +unit.eclipse.description = Bắn hai tia laser xuyên giáp và pháo chùm vào mọi kẻ địch. +unit.mono.description = Tự động khai thác đồng và chì, và vận chuyển vào lõi. unit.poly.description = Tự động xây dựng lại các công trình bị hỏng và hỗ trợ các đơn vị khác thi công. -unit.mega.description = Tự động sửa chữa các công trình bị hỏng. Có khả năng mang bộ binh nhỏ. -unit.quad.description = Thả bom to lên kẻ địch, sửa chữa các tòa nhà và tổn hại kẻ địch. Có khả năng mang bộ binh vừa. +unit.mega.description = Tự động sửa chữa các công trình bị hỏng. Có khả năng mang một số khối và bộ binh nhỏ. +unit.quad.description = Thả bom plasma to lên kẻ địch, sửa chữa các công trình và sát thương kẻ địch. Có khả năng mang bộ binh vừa. unit.oct.description = Bảo vệ đồng minh với giáp. Có khả năng mang đa số bộ binh. -unit.risso.description = Bắn chùm tên lửa và đạn lên kẻ địch tầm gần. -unit.minke.description = Bắn đạn và đạn thường lên kẻ địch tầm gần trên mặt đất. +unit.risso.description = Bắn chùm tên lửa và đạn lên kẻ địch. +unit.minke.description = Bắn chùm đạn và đạn tiêu chuẩn lên kẻ địch trên mặt đất. unit.bryde.description = Bắn đạn tầm xa và tên lửa vào kẻ địch. unit.sei.description = Bắn chùm tên lửa và đạn xuyên giáp vào kẻ địch. -unit.omura.description = Bắn đạn từ trường xuyên giáp tầm xa vào kẻ địch. Tạo nên Flare. -unit.alpha.description = Bảo vệ căn cứ cơ sở khỏi kẻ thù. Có thể xây dựng. -unit.beta.description = Bảo vệ căn cứ trụ sở khỏi kẻ thù. Có thể xây dựng. -unit.gamma.description = Bảo vệ căn cứ trung tâm khỏi kẻ thù. Có thể xây dựng. +unit.omura.description = Bắn đạn từ trường xuyên giáp tầm xa vào kẻ địch. Tạo ra Flare. +unit.alpha.description = Bảo vệ lõi Cơ sở khỏi kẻ thù. Có thể xây dựng. +unit.beta.description = Bảo vệ lõi Trụ sở khỏi kẻ thù. Có thể xây dựng. +unit.gamma.description = Bảo vệ lõi Trung tâm khỏi kẻ thù. Có thể xây dựng. unit.retusa.description = Bắn ngư lôi dẫn đường vào kẻ thù gần đó. Sửa đơn vị đồng minh. unit.oxynoe.description = Bắn luồng tia lửa sửa công trình vào kẻ địch gần đó. Nhắm vào viên đạn kẻ địch với các bệ súng phòng thủ mũi nhọn. unit.cyerce.description = Bắn chùm tên lửa dẫn đường lên kẻ thù. Sửa đơn vị đồng minh. unit.aegires.description = Gây sốc lên tất cả đơn vị và công trình bên trong trường năng lượng của nó. Sửa chữa tất cả đồng minh. unit.navanax.description = Bắn tia xung điện từ (EMP) nổ, gây thiệt hại đáng kể lên mạng lưới năng lượng và sửa chữa công trình đồng minh. Nung chảy kẻ địch ở gần với 4 bệ súng laser tự hành. + +#Erekir unit.stell.description = Bắn các viên đạn tiêu chuẩn vào mục tiêu kẻ thù. unit.locus.description = Bắn các viên đạn xen kẽ vào mục tiêu kẻ thù. unit.precept.description = Bắn các viên đạn phân cụm xuyêm thấu vào mục tiêu kẻ thù. @@ -2286,56 +2319,57 @@ unit.collaris.description = Bắn pháo phân mảnh tầm xa vào mục tiêu k unit.elude.description = Bắn một cặp viên đạn dẫn đường vào mục tiêu kẻ thù. Có thể lướt trên mặt chất lỏng. unit.avert.description = Bắn một cặp viên đạn xoắn vào mục tiêu kẻ thù. unit.obviate.description = Bắn một cặp cầu điện xoắn vào mục tiêu kẻ thù. -unit.quell.description = Bắn tên lửa tầm xa vào mục tiêu kẻ thù. Ngăn chặn khối công trình sửa chữa kẻ địch. -unit.disrupt.description = Bắn tên lửa oanh tạc tầm xa vào mục tiêu kẻ thù. Ngăn chặn khối công trình sửa chữa kẻ địch. -unit.evoke.description = Xây công trình để phòng thủ lõi Bastion. Sửa các công trình với chùm tia sáng. -unit.incite.description = Xây công trình để phòng thủ lõi Citadel. Sửa các công trình với chùm tia sáng. -unit.emanate.description = Xây công trình để phòng thủ lõi Acropolis. Sửa các công trình với chùm tia sáng. +unit.quell.description = Bắn tên lửa dẫn đường tầm xa vào mục tiêu kẻ thù. Ngăn chặn khối công trình sửa chữa kẻ địch. +unit.disrupt.description = Bắn tên lửa oanh tạc dẫn đường tầm xa vào mục tiêu kẻ thù. Ngăn chặn khối công trình sửa chữa kẻ địch. +unit.evoke.description = Xây công trình để phòng thủ lõi Pháo đài. Sửa các công trình với chùm tia sáng. Có khả năng nâng khối công trình 2x2. +unit.incite.description = Xây công trình để phòng thủ lõi Thủ phủ. Sửa các công trình với chùm tia sáng. Có khả năng nâng khối công trình 2x2. +unit.emanate.description = Xây công trình để phòng thủ lõi Đại đô. Sửa các công trình với chùm tia sáng. Có khả năng nâng khối công trình 2x2. lst.read = Đọc một số từ bộ nhớ được liên kết. lst.write = Ghi một số vào bộ nhớ được liên kết. -lst.print = Thêm văn bản vào bộ nhớ in.\nKhông hiển thị gì cho đến khi sử dụng [accent]Print Flush[]. -lst.format = Thay thế kí tự giữ chỗ tiếp theo ("[accent]@[]") trong bộ đệm văn bản bằng giá trị.\nVí dụ:\n[accent]print "test @"\nformat "example" -lst.draw = Thêm một thao tác vào bộ nhớ vẽ.\nKhông hiển thị gì cho đến khi sử dụng [accent]Draw Flush[]. +lst.print = Thêm văn bản vào bộ đệm in.\nKhông hiển thị gì cho đến khi sử dụng [accent]Print Flush[]. +lst.format = Thay thế từ giữ chỗ tiếp theo trong bộ đệm văn bản bằng giá trị.\nKhông làm gì nếu khuôn mẫu từ giữ chỗ không hợp lệ.\nKhuôn mẫu từ giữ chỗ: "{[accent] số 0-9[]}"\nVí dụ:\n[accent]print "test {0}"\nformat "example" +lst.draw = Thêm một thao tác vào bộ đệm vẽ.\nKhông hiển thị gì cho đến khi sử dụng [accent]Draw Flush[]. lst.drawflush = Chuyển các thao tác [accent]Draw[] đến màn hình. lst.printflush = Chuyển các thao tác [accent]Print[] đến khối tin nhắn. -lst.getlink = Nhận liên kết bộ xử lý theo thứ tự. Bắt đầu từ 0. -lst.control = Điều khiển một khối. +lst.getlink = Nhận liên kết bộ xử lý theo chỉ số. Bắt đầu từ 0. +lst.control = Điều khiển một công trình. lst.radar = Định vị các đơn vị trong phạm vi xung quanh một khối. lst.sensor = Lấy dữ liệu từ một khối hoặc đơn vị. lst.set = Đặt một biến. lst.operation = Thực hiện thao tác trên 1-2 biến. lst.end = Chuyển đến lệnh đầu tiên. -lst.wait = Chờ trong khoảng thời gian nhất định. +lst.wait = Chờ trong số giây nhất định. lst.stop = Ngừng thực thi khối xử lý này. -lst.lookup = Tra cứu một kiểu món đồ/chất lỏng/khối bởi định danh (ID).\nTổng số đếm của mỗi kiểu có thể truy xuất qua:\n[accent]@unitCount[] / [accent]@itemCount[] / [accent]@liquidCount[] / [accent]@blockCount[] +lst.lookup = Tra cứu một kiểu món đồ/chất lỏng/khối bởi định danh (ID).\nTổng số đếm của mỗi kiểu có thể truy xuất qua:\n[accent]@unitCount[] / [accent]@itemCount[] / [accent]@liquidCount[] / [accent]@blockCount[]\nĐể thao tác ngược lại, dùng sense [accent]@id[] của vật thể đó. lst.jump = Chuyển qua lệnh khác nếu điều kiện đúng. -lst.unitbind = Ràng buộc đơn vị tiếp theo của một kiểu, và lưu nó trong [accent]@unit[]. -lst.unitcontrol = Điều khiển đơn vị đang ràng buộc. -lst.unitradar = Xác định đơn vị xung quanh đơn bị đang ràng buộc. -lst.unitlocate = Xác định một kiểu cố định của vị trí/công trình bất kì đâu trên bản đồ.\nYêu cầu một đơn vị đang ràng buộc. -lst.getblock = Lấy dữ liệu từ ô từ vị trí bất kì. -lst.setblock = Chỉnh sửa dữ liệu từ ô từ vị trí bất kì. -lst.spawnunit = Tạo ra quân từ vị trí. +lst.unitbind = Gắn kết đơn vị tiếp theo của một kiểu, và lưu nó trong [accent]@unit[]. +lst.unitcontrol = Điều khiển đơn vị đang gắn kết. +lst.unitradar = Xác định đơn vị xung quanh đơn bị đang gắn kết. +lst.unitlocate = Xác định một kiểu cố định của vị trí/công trình bất kì đâu trên bản đồ.\nYêu cầu một đơn vị đang gắn kết. +lst.getblock = Lấy dữ liệu của ô tại vị trí bất kì. +lst.setblock = Chỉnh sửa dữ liệu của ô tại vị trí bất kì. +lst.spawnunit = Tạo ra quân tại một vị trí. lst.applystatus = Áp dụng hoặc loại bỏ một hiệu ứng trạng thái cho một đơn vị. lst.weathersense = Kiểm tra kiểu thời tiết đang hoạt động. lst.weatherset = Thiết lập trạng thái hiện tại của kiểu thời tiết. -lst.spawnwave = Mô phỏng một lượt xuất hiện ở vị trí tùy ý.\nSẽ không tăng số đếm lượt. +lst.spawnwave = Làm xuất hiện lượt tấn công. lst.explosion = Tạo ra một vụ nổ tại vị trí đó. -lst.setrate = Đặt tốc độ thực thi khối xử lý theo chỉ thị/tíc-tắc. +lst.setrate = Đặt tốc độ thực thi khối xử lý theo chỉ lệnh/tích-tắc. lst.fetch = Tra cứu các đơn vị, lõi, người chơi hoặc công trình bởi chỉ số.\nCác chỉ số bắt đầu từ 0 và kết thúc tại số lượng đếm của chúng. lst.packcolor = Đóng gói màu thành phần RGBA [0, 1] thành một số đơn dùng cho vẽ hoặc thiết lập quy tắc. lst.setrule = Đặt một quy tắc của trò chơi. -lst.flushmessage = Hiển thị một tin nhắn trên màn hình từ bộ đệm văn bản.\nSẽ đợt cho đến khi tin nhắn trước đó hoàn tất. +lst.flushmessage = Hiển thị một tin nhắn trên màn hình từ bộ đệm văn bản.\nSẽ đợi cho đến khi tin nhắn trước đó hoàn tất. lst.cutscene = Điều khiển góc máy quay của người chơi. lst.setflag = Đặt một cờ toàn cục mà có thể đọc được bởi tất cả khối xử lý. lst.getflag = Kiểm tra nếu cờ toàn cục được đặt. lst.setprop = Đặt một thuộc tính của đơn vị hoặc công trình. lst.effect = Tạo một phần hiệu ứng nhỏ. -lst.sync = Đồng bộ giá trị biến qua mạng.\nChỉ gọi tối đa 10 lần mỗi giây. -lst.makemarker = Tạo mới điểm đánh dấu lô-gic trên thế giới.\n Một định danh cho điểm đánh dấu này phải được cung cấp.\nĐiểm đánh dấu hiện tại bị giới hạn 20,000 trên mỗi thế giới. -lst.setmarker = Lập một thuộc tính cho một điểm đánh dấu.\n Định danh phải giống như định danh ở chỉ dẫn lệnh Tạo Điểm đánh dấu (Make Marker).\nGiá trị [accent]null [] sẽ bị phớt lờ thay vì chuyển thành 0. +lst.sync = Đồng bộ giá trị biến qua mạng.\nChỉ gọi tối đa 20 lần mỗi giây cho mỗi biến. +lst.makemarker = Tạo mới điểm đánh dấu logic trên thế giới.\n Một định danh cho điểm đánh dấu này phải được cung cấp.\nĐiểm đánh dấu hiện tại bị giới hạn 20,000 trên mỗi thế giới. +lst.setmarker = Lập một thuộc tính cho một điểm đánh dấu.\n Định danh phải giống như định danh ở chỉ lệnh Tạo Điểm đánh dấu (Make Marker).\nGiá trị [accent]null [] sẽ bị phớt lờ. lst.localeprint = Thêm một giá trị thuộc tính ngôn ngữ của bản đồ vào bộ đệm văn bản.\nĐể thiết đặt gói ngôn ngữ trong trình chỉnh sửa bản đồ, xem qua [accent]Thông tin bản đồ > Gói ngôn ngữ[].\nNếu máy khách là một thiết bị di động, sẽ cố in thuộc tính kết thúc bằng ".mobile" trước tiên. + lglobal.false = 0 lglobal.true = 1 lglobal.null = null @@ -2343,6 +2377,7 @@ lglobal.@pi = Hằng số toán học pi (3.141...) lglobal.@e = Hằng số toán học e (2.718...) lglobal.@degToRad = Nhân với số này để chuyển từ độ (deg) sang radian (rad) lglobal.@radToDeg = Nhân với số này để chuyển từ radian (rad) sang độ (deg) + lglobal.@time = Thời gian chơi của bản lưu hiện tại, tính bằng mili-giây lglobal.@tick = Thời gian chơi của bản lưu hiện tại, tính bằng tích-tắc (1 giây = 60 tích-tắc) lglobal.@second = Thời gian chơi của bản lưu hiện tại, tính bằng giây @@ -2351,51 +2386,57 @@ lglobal.@waveNumber = Số lượt hiện tại, nếu chế độ lượt đư lglobal.@waveTime = Thời gian đếm ngược của lượt, tính bằng giây lglobal.@mapw = Độ rộng bản đồ, tính bằng ô lglobal.@maph = Độ cao bản đồ, tính bằng ô + lglobal.sectionMap = Bản đồ lglobal.sectionGeneral = Chung lglobal.sectionNetwork = Mạng/Máy khách [Chỉ Bộ xử lý thế giới] lglobal.sectionProcessor = Bộ xử lý lglobal.sectionLookup = Tra cứu -lglobal.@this = Khối lô-gíc đang thực thi đoạn mã + +lglobal.@this = Khối logic đang thực thi đoạn mã lglobal.@thisx = Tọa độ x của khối đang thực thi đoạn mã lglobal.@thisy = Tọa độ y của khối đang thực thi đoạn mã lglobal.@links = Tổng số khối đã liên kết đến bộ xử lý này lglobal.@ipt = Tốc độ thực thi của bộ xử lý tính bằng lệnh mỗi tích-tắc (60 tích-tắc = 1 giây) + lglobal.@unitCount = Tổng số kiểu mẫu đơn vị trong trò chơi; được dùng với lệnh tra cứu lglobal.@blockCount = Tổng số kiểu mẫu khối trong trò chơi; được dùng với lệnh tra cứu lglobal.@itemCount = Tổng số kiểu mẫu vật phẩm trong trò chơi; được dùng với lệnh tra cứu lglobal.@liquidCount = Tổng số kiểu mẫu chất lỏng trong trò chơi; được dùng với lệnh tra cứu + lglobal.@server = True nếu đoạn mã chạy trên máy chủ hoặc chơi đơn, ngược lại là false lglobal.@client = True nếu đoạn mã chạy trên máy khách được kết nối đến máy chủ + lglobal.@clientLocale = Ngôn ngữ của máy khách đang chạy đoạn mã. Ví dụ: vi_VN lglobal.@clientUnit = Đơn vị máy khách đang chạy đoạn mã lglobal.@clientName = Tên người chơi của máy khách đang chạy đoạn mã lglobal.@clientTeam = Định danh đội của máy khách đang chạy đoạn mã lglobal.@clientMobile = True nếu máy khách đang chạy đoạn mã trên thiết bị di động, ngược lại là false -logic.nounitbuild = [red]Lô-gíc xây dựng đơn vị không được phép ở đây. +logic.nounitbuild = [red]Logic xây dựng đơn vị không được phép ở đây. lenum.type = Kiểu của công trình/đơn vị.\n Ví dụ, cho Bộ phân phát (router), nó sẽ trả về [accent]@router[].\nKhông phải một chuỗi. -lenum.shoot = Bắn vào vị trí xác định. +lenum.shoot = Bắn một vị trí. lenum.shootp = Bắn vào một đơn vị/công trình với tốc độ dự đoán. lenum.config = Cấu hình công trinh, kiểu như đồ của Khối sắp xếp. -lenum.enabled = Bất cứ khi nào khối hoạt động. +lenum.enabled = Khối có đang hoạt động. laccess.color = Màu đèn chiếu sáng. -laccess.controller = Thứ điều khiển đơn vị. Nếu khối xử lý đã điều khiển, trả về khối xử lý.\nNếu trong đội hình, trả về đơn vị dẫn đầu.\nNgược lại, trả về chính đơn vị đó. +laccess.controller = Thứ điều khiển đơn vị. Nếu khối xử lý đã điều khiển, trả về khối xử lý.\nNgược lại, trả về chính đơn vị đó. laccess.dead = Đơn vị/công trình đã chết hoặc không còn hợp lệ hay không. -laccess.controlled = Trả về:\n[accent]@ctrlProcessor[] nếu điều khiển là khối xử lý\n[accent]@ctrlPlayer[] nếu người điều khiển đơn vị/công trình là người chơi\n[accent]@ctrlFormation[] nếu đơn vị trọng đội hình\nNgược lại, 0. +laccess.controlled = Trả về:\n[accent]@ctrlProcessor[] nếu điều khiển là khối xử lý\n[accent]@ctrlPlayer[] nếu người điều khiển đơn vị/công trình là người chơi\n[accent]@ctrlCommand[] nếu đơn vị được điều khiển qua mệnh lệnh của người chơi\nNgược lại, 0. laccess.progress = Tiến trình thực hiện, 0 đến 1.\n Trả về tiến trình sản xuất, nạp đạn bệ súng hoặc xây dựng. -laccess.speed = Tốc độ của đơn vị, tính bằng ô/giây. +laccess.speed = Tốc độ cao nhất của đơn vị, tính bằng ô/giây. laccess.id = Định danh của một đơn vị/khối/vật phẩm/chất lỏng.\nViệc này làm ngược lại với thao tác tra cứu. -lcategory.unknown = Không xác định -lcategory.unknown.description = Chỉ thị không được phân loại. + +lcategory.unknown = Không rõ +lcategory.unknown.description = Chỉ lệnh không được phân loại. lcategory.io = Đầu Vào & Ra lcategory.io.description = Chỉnh sửa nội dung của khối ô nhớ và bộ đệm khối xử lý. lcategory.block = Điều khiển khối lcategory.block.description = Tương tác với khối lcategory.operation = Các phép toán -lcategory.operation.description = Các phép toán lô-gíc. +lcategory.operation.description = Các phép toán logic. lcategory.control = Điều khiển luồng thực thi lcategory.control.description = Quản lý trình tự thực thi. lcategory.unit = Điều khiển đơn vị @@ -2403,7 +2444,7 @@ lcategory.unit.description = Ra lệnh cho đơn vị. lcategory.world = Thế giới lcategory.world.description = Kiểm soát hành vi của thế giới. -graphicstype.clear = Tô màu cho màn hình. +graphicstype.clear = Tô màu cho toàn màn hình. graphicstype.color = Đặt màu cho thao tác vẽ tiếp theo. graphicstype.col = Tương tự màu, nhưng được đóng gói.\nMàu đã đóng gói được viết theo mã thập lục phân với tiền tố [accent]%[].\nVí dụ: [accent]%ff0000[] sẽ là màu đỏ. graphicstype.stroke = Đặt chiều rộng đoạn thẳng. @@ -2414,7 +2455,7 @@ graphicstype.poly = Tô vào đa giác đều. graphicstype.linepoly = Vẽ đường viền đa giác đều. graphicstype.triangle = Tô một hình tam giác. graphicstype.image = Vẽ hình ảnh một số nội dung.\nVí dụ: [accent]@router[] hoặc [accent]@dagger[]. -graphicstype.print = Vẽ văn bản từ bộ đệm in ra.\nLàm sạch bộ đệm. +graphicstype.print = Vẽ văn bản từ bộ đệm in ra.\nChỉ được phép dùng các kí tự ASCII.\nLàm sạch bộ đệm. lenum.always = Luôn đúng. lenum.idiv = Chia lấy phần nguyên. @@ -2426,10 +2467,10 @@ lenum.strictequal = Bằng nhau ràng buộc. Không ép kiểu.\nCó thể dùn lenum.shl = Nhảy bit sang trái. lenum.shr = Nhảy bit sang phải. lenum.or = Phép toán bit OR. -lenum.land = Lô-gíc AND. +lenum.land = Phép toán logic AND. lenum.and = Phép toán bit AND. lenum.not = Phép toán lật bit. -lenum.xor = Phép toán XOR. +lenum.xor = Phép toán bit XOR. lenum.min = Số nhỏ nhất giữa hai số. lenum.max = Số lớn nhất giữa hai số. @@ -2467,7 +2508,7 @@ lenum.damaged = Công trình bị hư tổn của đồng minh. lenum.spawn = Điểm xuất hiện của kẻ địch.\nCó thể là một lõi hoặc một vị trí. lenum.building = Công trình trong nhóm nhất định. -lenum.core = Bất kì căn cứ. +lenum.core = Bất kỳ lõi nào. lenum.storage = Khối lưu trữ, Ví dụ Nhà kho. lenum.generator = Khối có thể tạo ra năng lượng. lenum.factory = Khối có thể biến đổi vật phẩm. @@ -2482,13 +2523,13 @@ sensor.in = Công trình/Đơn vị để cảm biến. radar.from = Công trình để cảm biến.\nPhạm vi cảm biến bị giới hạn bởi phạm vi của công trình. radar.target = Bộ lọc cho các đơn vị được cảm biến. radar.and = Bộ lọc bổ sung. -radar.order = Sắp xếp theo thứ tự. đặt giá trị 0 để đảo ngược. +radar.order = Sắp xếp theo thứ tự. 0 để đảo ngược. radar.sort = Số liệu để kết quả sắp xếp theo. radar.output = Biến để ghi đơn vị được xuất ra. unitradar.target = Bộ lọc cho các đơn vị được cảm biến. unitradar.and = Bộ lọc bổ sung. -unitradar.order = Sắp xếp theo thứ tự. đặt giá trị 0 để đảo ngược. +unitradar.order = Sắp xếp theo thứ tự. 0 để đảo ngược. unitradar.sort = Số liệu để kết quả sắp xếp theo. unitradar.output = Biến để ghi đơn vị được xuất ra. @@ -2505,10 +2546,10 @@ unitlocate.group = Nhóm công trình để tìm kiếm. lenum.idle = Không di chuyển, nhưng vẫn xây dựng/đào.\nTrạng thái mặc định. lenum.stop = Dừng di chuyển/Đào/Xây dựng. -lenum.unbind = Vô hiệu hóa hoàn toàn điều khiển lô-gíc.\n Khôi phục AI tiêu chuẩn. +lenum.unbind = Vô hiệu hóa hoàn toàn điều khiển logic.\n Khôi phục AI tiêu chuẩn. lenum.move = Di chuyển đến vị trí xác định. lenum.approach = Tiếp cận một vị trí với bán kính. -lenum.pathfind = Tìm đường đến nơi tạo ra kẻ địch. +lenum.pathfind = Tìm đường đến một vị trí xác định. lenum.autopathfind = Tự động tìm đường đến lõi hoặc nơi hạ cánh gần nhất của kẻ địch.\n Việc này tương tự như việc tìm đường của kẻ địch trong các lượt. lenum.target = Bắn vào vị trí xác định. lenum.targetp = Bắn vào một mục tiêu với tốc độ dự đoán @@ -2523,10 +2564,11 @@ lenum.build = Xây công trình. lenum.getblock = Lấy một cấu trúc và kiểu tại một tọa độ.\nĐơn vị phải nằm trong tầm của vị trí.\nKhối rắn không phải công trình có kiểu [accent]@solid[]. lenum.within = Kiểm tra xem đơn vị có gần vị trí không. lenum.boost = Bắt đầu/Dừng tăng tốc. -lenum.flushtext = Lấp đầy nội dung của bộ đệm cho điểm đánh dấu, nếu có thể áp dụng.\n Nếu [accent]fetch[] gắn là [accent]true[], sẽ cố truy vấn các thuộc tính từ gói ngôn ngữ của bản đồ hoặc trò chơi. -lenum.texture = Tên kết cấu lấy thẳng từ bản khung kết cấu của trò chơi (dùng kiểu tên kebab-case, tên-chữ-thường-chứa-gạch-nối).\nNếu printFlush gán bằng true, sẽ sử dụng nội dung bộ đệm văn bản làm tham số văn bản. + +lenum.flushtext = Lấp đầy nội dung của bộ đệm cho điểm đánh dấu, nếu có thể áp dụng.\n Nếu [accent]fetch[] đặt là [accent]true[], sẽ cố truy vấn các thuộc tính từ gói ngôn ngữ của bản đồ hoặc trò chơi. +lenum.texture = Tên kết cấu lấy thẳng từ bản khung kết cấu của trò chơi (dùng kiểu tên kebab-case, tên-chữ-thường-chứa-gạch-nối).\nNếu printFlush đặt là true, sẽ sử dụng nội dung bộ đệm văn bản làm tham số văn bản. lenum.texturesize = Kích thước của kết cấu bằng ô. Giá trị 0 chia tỷ lệ chiều rộng của điểm đánh dấu theo kích thước của kết cấu ban đầu. lenum.autoscale = Có chia tỷ lệ điểm đánh dấu tương ứng với mức thu phóng của người chơi hay không. lenum.posi = Vị trí theo chỉ số, dùng cho điểm đánh dấu đường kẻ (line) và bốn điểm (quad) với 0 là vị trí đầu tiên. lenum.uvi = Vị trí của kết cấu trong phạm vi từ 0 đến 1, dùng cho đánh dấu bốn điểm (quad). -lenum.colori = Màu theo chỉ số, dùng cho điểm đánh dấu đường kẻ (line) và bốn điểm (quad) với 0 là màu đầu tiên. +lenum.colori = Màu theo chỉ số, dùng cho điểm đánh dấu đường kẻ (line) và bốn điểm (quad) với 0 là màu đầu tiên. \ No newline at end of file From bc6493c54fcc3c7d8398af8b3be7e04807b7d087 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Sun, 14 Apr 2024 14:06:58 +0000 Subject: [PATCH 102/348] Automatic bundle update --- core/assets/bundles/bundle_vi.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index 11550e1885..7856f9339e 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -2571,4 +2571,4 @@ lenum.texturesize = Kích thước của kết cấu bằng ô. Giá trị 0 chi lenum.autoscale = Có chia tỷ lệ điểm đánh dấu tương ứng với mức thu phóng của người chơi hay không. lenum.posi = Vị trí theo chỉ số, dùng cho điểm đánh dấu đường kẻ (line) và bốn điểm (quad) với 0 là vị trí đầu tiên. lenum.uvi = Vị trí của kết cấu trong phạm vi từ 0 đến 1, dùng cho đánh dấu bốn điểm (quad). -lenum.colori = Màu theo chỉ số, dùng cho điểm đánh dấu đường kẻ (line) và bốn điểm (quad) với 0 là màu đầu tiên. \ No newline at end of file +lenum.colori = Màu theo chỉ số, dùng cho điểm đánh dấu đường kẻ (line) và bốn điểm (quad) với 0 là màu đầu tiên. From 569d0f8fac401f09809513987cf67dd5f4148c05 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 14 Apr 2024 11:17:57 -0400 Subject: [PATCH 103/348] Fixed #9747 / Fixed #9746 --- core/src/mindustry/entities/comp/BuildingComp.java | 6 +++++- core/src/mindustry/entities/comp/UnitComp.java | 2 +- core/src/mindustry/input/InputHandler.java | 4 ++-- core/src/mindustry/ui/dialogs/AdminsDialog.java | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/core/src/mindustry/entities/comp/BuildingComp.java b/core/src/mindustry/entities/comp/BuildingComp.java index a8d027cf1e..f7c114ab6d 100644 --- a/core/src/mindustry/entities/comp/BuildingComp.java +++ b/core/src/mindustry/entities/comp/BuildingComp.java @@ -1310,7 +1310,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, if(value instanceof UnitType) type = UnitType.class; if(builder != null && builder.isPlayer()){ - lastAccessed = builder.getPlayer().coloredName(); + updateLastAccess(builder.getPlayer()); } if(block.configurations.containsKey(type)){ @@ -1324,6 +1324,10 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, } } + public void updateLastAccess(Player player){ + lastAccessed = player.coloredName(); + } + /** Called when the block is tapped by the local player. */ public void tapped(){ diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index 061d323858..8ca3361cdd 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -734,7 +734,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I /** @return name of direct or indirect player controller. */ @Override public @Nullable String getControllerName(){ - if(isPlayer()) return getPlayer().name; + if(isPlayer()) return getPlayer().coloredName(); if(controller instanceof LogicAI ai && ai.controller != null) return ai.controller.lastAccessed; return null; } diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index e4ba051bac..b280a958be 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -391,7 +391,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ if(build == null || build.team() != player.team() || !build.block.commandable) continue; build.onCommand(target); - build.lastAccessed = player.name; + build.updateLastAccess(player); if(!state.isPaused() && player == Vars.player){ Fx.moveCommand.at(target); @@ -596,7 +596,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ throw new ValidateException(player, "Player cannot rotate a block."); } - if(player != null) build.lastAccessed = player.name; + if(player != null) build.updateLastAccess(player); int previous = build.rotation; build.rotation = Mathf.mod(build.rotation + Mathf.sign(direction), 4); build.updateProximity(); diff --git a/core/src/mindustry/ui/dialogs/AdminsDialog.java b/core/src/mindustry/ui/dialogs/AdminsDialog.java index 04bdf379d7..0d3eedba10 100644 --- a/core/src/mindustry/ui/dialogs/AdminsDialog.java +++ b/core/src/mindustry/ui/dialogs/AdminsDialog.java @@ -1,5 +1,6 @@ package mindustry.ui.dialogs; +import arc.*; import arc.scene.ui.*; import arc.scene.ui.layout.*; import mindustry.gen.*; @@ -39,7 +40,7 @@ public class AdminsDialog extends BaseDialog{ res.labelWrap("[lightgray]" + info.lastName).width(w - h - 24f); res.add().growX(); res.button(Icon.cancel, () -> { - ui.showConfirm("@confirm", "@confirmunadmin", () -> { + ui.showConfirm("@confirm", Core.bundle.format("@confirmunadmin", info.lastName), () -> { netServer.admins.unAdminPlayer(info.id); Groups.player.each(player -> { if(player != null && !player.isLocal() && player.uuid().equals(info.id)){ From 9333aa95996a6f9f627ee8ae6f92495854f71554 Mon Sep 17 00:00:00 2001 From: NealRead <160118416+NealRead@users.noreply.github.com> Date: Mon, 15 Apr 2024 00:35:35 +0700 Subject: [PATCH 104/348] Update servers_v7.json (#9748) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 4bf64d8b48..1fcaf20e7a 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -291,7 +291,7 @@ }, { "name": "NPTS", - "address": ["81.94.156.54"] + "address": ["178.20.45.59"] }, { "name": "Skywar.VN", From 0be74922b0a31f3365cd6ce9d99e5717dd2d8e11 Mon Sep 17 00:00:00 2001 From: Leo <86385005+Leo-MathGuy@users.noreply.github.com> Date: Mon, 15 Apr 2024 18:03:59 +0500 Subject: [PATCH 105/348] Added offset to DrawFlame (#9749) --- core/src/mindustry/world/draw/DrawFlame.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/mindustry/world/draw/DrawFlame.java b/core/src/mindustry/world/draw/DrawFlame.java index 0c6d3c41b3..60af38b497 100644 --- a/core/src/mindustry/world/draw/DrawFlame.java +++ b/core/src/mindustry/world/draw/DrawFlame.java @@ -14,6 +14,7 @@ public class DrawFlame extends DrawBlock{ public TextureRegion top; public float lightRadius = 60f, lightAlpha = 0.65f, lightSinScl = 10f, lightSinMag = 5; public float flameRadius = 3f, flameRadiusIn = 1.9f, flameRadiusScl = 5f, flameRadiusMag = 2f, flameRadiusInMag = 1f; + public float flameX = 0, flameY = 0; public DrawFlame(){ } @@ -43,9 +44,9 @@ public class DrawFlame extends DrawBlock{ Draw.alpha(((1f - g) + Mathf.absin(Time.time, 8f, g) + Mathf.random(r) - r) * build.warmup()); Draw.tint(flameColor); - Fill.circle(build.x, build.y, flameRadius + Mathf.absin(Time.time, flameRadiusScl, flameRadiusMag) + cr); + Fill.circle(build.x + flameX, build.y + flameY, flameRadius + Mathf.absin(Time.time, flameRadiusScl, flameRadiusMag) + cr); Draw.color(1f, 1f, 1f, build.warmup()); - Fill.circle(build.x, build.y, flameRadiusIn + Mathf.absin(Time.time, flameRadiusScl, flameRadiusInMag) + cr); + Fill.circle(build.x + flameX, build.y + flameY, flameRadiusIn + Mathf.absin(Time.time, flameRadiusScl, flameRadiusInMag) + cr); Draw.color(); } @@ -53,6 +54,6 @@ public class DrawFlame extends DrawBlock{ @Override public void drawLight(Building build){ - Drawf.light(build.x, build.y, (lightRadius + Mathf.absin(lightSinScl, lightSinMag)) * build.warmup() * build.block.size, flameColor, lightAlpha); + Drawf.light(build.x + flameX, build.y + flameY, (lightRadius + Mathf.absin(lightSinScl, lightSinMag)) * build.warmup() * build.block.size, flameColor, lightAlpha); } } From b7fab3839afa4210f6c2cf19680680bf526c098c Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 15 Apr 2024 12:54:07 -0400 Subject: [PATCH 106/348] Added Mod#getConfigFolder --- core/src/mindustry/mod/Mod.java | 7 ++++++- core/src/mindustry/mod/Mods.java | 12 +++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/core/src/mindustry/mod/Mod.java b/core/src/mindustry/mod/Mod.java index d56ea9d526..930f967a06 100644 --- a/core/src/mindustry/mod/Mod.java +++ b/core/src/mindustry/mod/Mod.java @@ -6,6 +6,11 @@ import mindustry.*; public abstract class Mod{ + /** @return the folder where configuration files for this mod should go.*/ + public Fi getConfigFolder(){ + return Vars.mods.getConfigFolder(this); + } + /** @return the config file for this plugin, as the file 'mods/[plugin-name]/config.json'.*/ public Fi getConfig(){ return Vars.mods.getConfig(this); @@ -26,7 +31,7 @@ public abstract class Mod{ } - /** Register any commands to be used on the client side, e.g. sent from an in-game player.. */ + /** Register any commands to be used on the client side, e.g. sent from an in-game player. */ public void registerClientCommands(CommandHandler handler){ } diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java index 25a6e1cc4c..9e8e6b2b2b 100644 --- a/core/src/mindustry/mod/Mods.java +++ b/core/src/mindustry/mod/Mods.java @@ -62,12 +62,18 @@ public class Mods implements Loadable{ return mainLoader; } - /** Returns a file named 'config.json' in a special folder for the specified plugin. + /** @return the folder where configuration files for this mod should go. The folder may not exist yet; call mkdirs() before writing to it. * Call this in init(). */ - public Fi getConfig(Mod mod){ + public Fi getConfigFolder(Mod mod){ ModMeta load = metas.get(mod.getClass()); if(load == null) throw new IllegalArgumentException("Mod is not loaded yet (or missing)!"); - return modDirectory.child(load.name).child("config.json"); + return modDirectory.child(load.name); + } + + /** @return a file named 'config.json' in the config folder for the specified mod. + * Call this in init(). */ + public Fi getConfig(Mod mod){ + return getConfigFolder(mod).child("config.json"); } /** Returns a list of files per mod subdirectory. */ From d9f981cbdeb57109a8a7b0e32535c74f51216ed3 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 15 Apr 2024 13:17:11 -0400 Subject: [PATCH 107/348] Version update --- core/src/mindustry/ai/HierarchyPathFinder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 972261fef3..4bc2efbf0a 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -29,7 +29,7 @@ public class HierarchyPathFinder implements Runnable{ static final int clusterSize = 12; - static final boolean debug = false;//OS.hasProp("mindustry.debug"); + static final boolean debug = OS.hasProp("mindustry.debug"); static final int[] offsets = { 1, 0, //right: bottom to top From 8eb6a1068b65a96a402293a2d9518ca3e96ab4b8 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 15 Apr 2024 16:31:07 -0400 Subject: [PATCH 108/348] Struct bool setter fix --- .../src/main/java/mindustry/annotations/impl/StructProcess.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/annotations/src/main/java/mindustry/annotations/impl/StructProcess.java b/annotations/src/main/java/mindustry/annotations/impl/StructProcess.java index bac713b019..955587301d 100644 --- a/annotations/src/main/java/mindustry/annotations/impl/StructProcess.java +++ b/annotations/src/main/java/mindustry/annotations/impl/StructProcess.java @@ -102,7 +102,7 @@ public class StructProcess extends BaseProcessor{ //bools: single bit, needs special case to clear things setter.beginControlFlow("if(value)"); - setter.addStatement("return ($T)(($L & ~(1L << $LL)) | (1L << $LL))", structType, structParam, offset, offset); + setter.addStatement("return ($T)($L | (1L << $LL))", structType, structParam, offset); setter.nextControlFlow("else"); setter.addStatement("return ($T)(($L & ~(1L << $LL)))", structType, structParam, offset); setter.endControlFlow(); From b8c6781004f20f399c877a06f668366e420eadc2 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 15 Apr 2024 17:20:48 -0400 Subject: [PATCH 109/348] Fixed #9750 --- .../src/mindustry/world/blocks/power/PowerGenerator.java | 4 ++-- .../mindustry/world/blocks/power/ThermalGenerator.java | 5 +++++ core/src/mindustry/world/blocks/sandbox/PowerSource.java | 9 +++++++++ core/src/mindustry/world/blocks/units/UnitAssembler.java | 4 ++-- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/core/src/mindustry/world/blocks/power/PowerGenerator.java b/core/src/mindustry/world/blocks/power/PowerGenerator.java index fb77b987a1..9b0ef63732 100644 --- a/core/src/mindustry/world/blocks/power/PowerGenerator.java +++ b/core/src/mindustry/world/blocks/power/PowerGenerator.java @@ -102,7 +102,7 @@ public class PowerGenerator extends PowerDistributor{ @Override public float warmup(){ - return productionEfficiency; + return enabled ? productionEfficiency : 0f; } @Override @@ -154,7 +154,7 @@ public class PowerGenerator extends PowerDistributor{ @Override public float getPowerProduction(){ - return powerProduction * productionEfficiency; + return enabled ? powerProduction * productionEfficiency : 0f; } @Override diff --git a/core/src/mindustry/world/blocks/power/ThermalGenerator.java b/core/src/mindustry/world/blocks/power/ThermalGenerator.java index 9195205a9f..9c71e77ca8 100644 --- a/core/src/mindustry/world/blocks/power/ThermalGenerator.java +++ b/core/src/mindustry/world/blocks/power/ThermalGenerator.java @@ -88,6 +88,11 @@ public class ThermalGenerator extends PowerGenerator{ } } + @Override + public float totalProgress(){ + return enabled ? super.totalProgress() : 0f; + } + @Override public void drawLight(){ Drawf.light(x, y, (40f + Mathf.absin(10f, 5f)) * Math.min(productionEfficiency, 2f) * size, Color.scarlet, 0.4f); diff --git a/core/src/mindustry/world/blocks/sandbox/PowerSource.java b/core/src/mindustry/world/blocks/sandbox/PowerSource.java index 3a8685bb5e..0e011c9bac 100644 --- a/core/src/mindustry/world/blocks/sandbox/PowerSource.java +++ b/core/src/mindustry/world/blocks/sandbox/PowerSource.java @@ -11,11 +11,20 @@ public class PowerSource extends PowerNode{ maxNodes = 100; outputsPower = true; consumesPower = false; + drawDisabled = true; //TODO maybe don't? envEnabled = Env.any; } public class PowerSourceBuild extends PowerNodeBuild{ + @Override + public void onProximityUpdate(){ + super.onProximityUpdate(); + if(!allowUpdate()){ + enabled = false; + } + } + @Override public float getPowerProduction(){ return enabled ? powerProduction : 0f; diff --git a/core/src/mindustry/world/blocks/units/UnitAssembler.java b/core/src/mindustry/world/blocks/units/UnitAssembler.java index f77f2119ac..2d2a40115e 100644 --- a/core/src/mindustry/world/blocks/units/UnitAssembler.java +++ b/core/src/mindustry/world/blocks/units/UnitAssembler.java @@ -373,12 +373,12 @@ public class UnitAssembler extends PayloadBlock{ units.clear(); } - float powerStatus = power == null ? 1f : power.status; + float powerStatus = !enabled ? 0f : power == null ? 1f : power.status; powerWarmup = Mathf.lerpDelta(powerStatus, powerStatus > 0.0001f ? 1f : 0f, 0.1f); droneWarmup = Mathf.lerpDelta(droneWarmup, units.size < dronesCreated ? powerStatus : 0f, 0.1f); totalDroneProgress += droneWarmup * delta(); - if(units.size < dronesCreated && (droneProgress += delta() * state.rules.unitBuildSpeed(team) * powerStatus / droneConstructTime) >= 1f){ + if(units.size < dronesCreated && enabled && (droneProgress += delta() * state.rules.unitBuildSpeed(team) * powerStatus / droneConstructTime) >= 1f){ if(!net.client()){ var unit = droneType.create(team); if(unit instanceof BuildingTetherc bt){ From 183c1a576378864c286b50e17ea731fc9e5b5f46 Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 17 Apr 2024 22:10:43 -0400 Subject: [PATCH 110/348] Flow field raycasting --- .../src/mindustry/ai/HierarchyPathFinder.java | 102 ++++++++++++------ core/src/mindustry/ai/types/CommandAI.java | 6 +- .../entities/units/AIController.java | 2 +- gradle.properties | 2 +- 4 files changed, 79 insertions(+), 33 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 4bc2efbf0a..e120bfefd2 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -295,6 +295,8 @@ public class HierarchyPathFinder implements Runnable{ }catch(Exception ignored){} //probably has some concurrency issues when iterating but I don't care, this is for debugging } }); + + Draw.reset(); }); } } @@ -1055,14 +1057,16 @@ public class HierarchyPathFinder implements Runnable{ int maxIterations = 30; //TODO higher/lower number? is this still too slow? int i = 0; boolean recalc = false; + unit.hitboxTile(Tmp.r3); + //tile rect size has tile size factored in, since the ray cannot have thickness + float tileRectSize = tilesize + Tmp.r3.height; //TODO last pos can change if the flowfield changes. if(initialTileOn.pos() != request.lastTile || request.lastTargetTile == null){ - //TODO tanks have weird behavior near edges of walls, as they try to avoid them boolean anyNearSolid = false; //find the next tile until one near a solid block is discovered - while(i ++ < maxIterations && !anyNearSolid){ + while(i ++ < maxIterations){ int value = getCost(fieldCache, old, tileOn.x, tileOn.y); Tile current = null; @@ -1083,16 +1087,16 @@ public class HierarchyPathFinder implements Runnable{ } //check for corner preventing movement - if((checkCorner(unit, tileOn, other, dir - 1) || checkCorner(unit, tileOn, other, dir + 1)) && - (checkSolid(unit, tileOn, dir - 2) || checkSolid(unit, tileOn, dir + 2))){ //there must be a tile to the left or right to keep the unit from going back and forth forever + //if((checkCorner(unit, tileOn, other, dir - 1) || checkCorner(unit, tileOn, other, dir + 1)) && + // (checkSolid(unit, tileOn, dir - 2) || checkSolid(unit, tileOn, dir + 2))){ //there must be a tile to the left or right to keep the unit from going back and forth forever - recalc = true; + //recalc = true; //keep moving even if it's blocked - any = true; - continue; - } + //any = true; + // continue; + //} - if(otherCost < value && otherCost != impassable && (otherCost != 0 || packed == destPos) && (current == null || otherCost < minCost) && passable(unit.team.id, cost, packed) && + if((value == 0 || otherCost < value) && otherCost != impassable && (otherCost != 0 || packed == destPos) && (current == null || otherCost < minCost) && passable(unit.team.id, cost, packed) && //diagonal corner trap !( (!passable(team, cost, world.packArray(tileOn.x + point.x, tileOn.y)) || @@ -1104,13 +1108,28 @@ public class HierarchyPathFinder implements Runnable{ } } + //TODO raycast spam = extremely slow if(!(current == null || (costId == costGround && current.dangerous() && !tileOn.dangerous()))){ - tileOn = current; - any = true; - if(current.array() == destPos){ + //when anyNearSolid is false, no solid tiles have been encountered anywhere so far, so raycasting is a waste of time + if(anyNearSolid && !tileOn.dangerous() && raycastRect(unit.x, unit.y, current.x * tilesize, current.y * tilesize, team, cost, initialTileOn.x, initialTileOn.y, current.x, current.y, tileRectSize)){ + + //TODO this may be a mistake + if(tileOn == initialTileOn){ + recalc = true; + any = true; + } + break; + }else{ + tileOn = current; + any = true; + + if(current.array() == destPos){ + break; + } } + }else{ break; } @@ -1156,24 +1175,6 @@ public class HierarchyPathFinder implements Runnable{ initializePathRequest(request, request.team, request.costId, request.unit.tileX(), request.unit.tileY(), request.destination % wwidth, request.destination / wwidth); } - private boolean checkSolid(Unit unit, Tile tile, int dir){ - var p = Geometry.d8[Mathf.mod(dir, 8)]; - return !unit.canPass(tile.x + p.x, tile.y + p.y); - } - - private boolean checkCorner(Unit unit, Tile tile, Tile next, int dir){ - Tile other = tile.nearby(Geometry.d8[Mathf.mod(dir, 8)]); - if(other == null){ - return true; - } - - if(!unit.canPass(other.x, other.y)){ - return Geometry.raycastRect(unit.x, unit.y, next.worldx(), next.worldy(), Tmp.r1.setCentered(other.worldx(), other.worldy(), tilesize).grow(Math.min(unit.hitSize * 0.66f, 7.6f))) != null; - } - - return false; - } - private int getCost(FieldCache cache, FieldCache old, int x, int y){ //fall back to the old flowfield when possible - it's best not to use partial results from the base cache if(old != null){ @@ -1244,6 +1245,47 @@ public class HierarchyPathFinder implements Runnable{ return true; } + private static boolean overlap(int team, PathCost type, int x, int y, float startX, float startY, float endX, float endY, float rectSize){ + if(x < 0 || y < 0 || x >= wwidth || y >= wheight) return false; + if(!passable(team, type, x + y * wwidth)){ + return Intersector.intersectSegmentRectangleFast(startX, startY, endX, endY, x * tilesize - rectSize/2f, y * tilesize - rectSize/2f, rectSize, rectSize); + } + return false; + } + + private static boolean raycastRect(float startX, float startY, float endX, float endY, int team, PathCost type, int x1, int y1, int x2, int y2, float rectSize){ + int ww = wwidth, wh = wheight; + int x = x1, dx = Math.abs(x2 - x), sx = x < x2 ? 1 : -1; + int y = y1, dy = Math.abs(y2 - y), sy = y < y2 ? 1 : -1; + int e2, err = dx - dy; + + while(x >= 0 && y >= 0 && x < ww && y < wh){ + if( + !passable(team, type, x + y * wwidth) || + overlap(team, type, x + 1, y, startX, startY, endX, endY, rectSize) || + overlap(team, type, x - 1, y, startX, startY, endX, endY, rectSize) || + overlap(team, type, x, y + 1, startX, startY, endX, endY, rectSize) || + overlap(team, type, x, y - 1, startX, startY, endX, endY, rectSize) + ) return true; + + if(x == x2 && y == y2) return false; + + //diagonal ver + e2 = 2 * err; + if(e2 > -dy){ + err -= dy; + x += sx; + } + + if(e2 < dx){ + err += dx; + y += sy; + } + } + + return true; + } + private static boolean avoid(int team, PathCost type, int tilePos){ int cost = cost(team, type, tilePos); return cost == impassable || cost >= 2; diff --git a/core/src/mindustry/ai/types/CommandAI.java b/core/src/mindustry/ai/types/CommandAI.java index 3be38e515a..042852e05f 100644 --- a/core/src/mindustry/ai/types/CommandAI.java +++ b/core/src/mindustry/ai/types/CommandAI.java @@ -205,6 +205,8 @@ public class CommandAI extends AIController{ } } + boolean alwaysArrive = false; + if(targetPos != null){ boolean move = true, isFinalPoint = commandQueue.size == 0; vecOut.set(targetPos); @@ -251,6 +253,8 @@ public class CommandAI extends AIController{ //if you've spent 3 seconds stuck, something is wrong, move regardless move = hpath.getPathPosition(unit, vecMovePos, targetPos, vecOut, noFound) && (!blockingUnit || timeSpentBlocked > maxBlockTime); + //rare case where unit must be perfectly aligned (happens with 1-tile gaps) + alwaysArrive = vecOut.epsilonEquals(unit.tileX() * tilesize, unit.tileY() * tilesize); //we've reached the final point if the returned coordinate is equal to the supplied input isFinalPoint &= vecMovePos.epsilonEquals(vecOut, 4.1f); @@ -278,7 +282,7 @@ public class CommandAI extends AIController{ attackTarget != null && unit.within(attackTarget, engageRange) && stance != UnitStance.ram ? engageRange : unit.isGrounded() ? 0f : attackTarget != null && stance != UnitStance.ram ? engageRange : - 0f, unit.isFlying() ? 40f : 100f, false, null, isFinalPoint); + 0f, unit.isFlying() ? 40f : 100f, false, null, isFinalPoint || alwaysArrive); } } diff --git a/core/src/mindustry/entities/units/AIController.java b/core/src/mindustry/entities/units/AIController.java index 009e56d6ad..6025ea611c 100644 --- a/core/src/mindustry/entities/units/AIController.java +++ b/core/src/mindustry/entities/units/AIController.java @@ -341,7 +341,7 @@ public class AIController implements UnitController{ vec.setLength(speed * length); } - //do not move when infinite vectors are used or if its zero. + //ignore invalid movement values if(vec.isNaN() || vec.isInfinite() || vec.isZero()) return; if(!unit.type.omniMovement && unit.type.rotateMoveFirst){ diff --git a/gradle.properties b/gradle.properties index 25be0e37a2..025014ab41 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=8a2decd656 +archash=e812c7a008 From dbb17b7f21dfe029a3835ca72150b6b35e3aa91b Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 17 Apr 2024 22:31:50 -0400 Subject: [PATCH 111/348] Reduced turning margin --- core/src/mindustry/ai/HierarchyPathFinder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index e120bfefd2..dce60a10a4 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -1059,7 +1059,7 @@ public class HierarchyPathFinder implements Runnable{ boolean recalc = false; unit.hitboxTile(Tmp.r3); //tile rect size has tile size factored in, since the ray cannot have thickness - float tileRectSize = tilesize + Tmp.r3.height; + float tileRectSize = tilesize + Tmp.r3.height - 0.1f; //TODO last pos can change if the flowfield changes. if(initialTileOn.pos() != request.lastTile || request.lastTargetTile == null){ From 95e4be7186e7b65f20839d6699554840b3447438 Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 17 Apr 2024 22:46:07 -0400 Subject: [PATCH 112/348] comments --- core/src/mindustry/ai/HierarchyPathFinder.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index dce60a10a4..de066116b8 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -1086,16 +1086,6 @@ public class HierarchyPathFinder implements Runnable{ anyNearSolid = true; } - //check for corner preventing movement - //if((checkCorner(unit, tileOn, other, dir - 1) || checkCorner(unit, tileOn, other, dir + 1)) && - // (checkSolid(unit, tileOn, dir - 2) || checkSolid(unit, tileOn, dir + 2))){ //there must be a tile to the left or right to keep the unit from going back and forth forever - - //recalc = true; - //keep moving even if it's blocked - //any = true; - // continue; - //} - if((value == 0 || otherCost < value) && otherCost != impassable && (otherCost != 0 || packed == destPos) && (current == null || otherCost < minCost) && passable(unit.team.id, cost, packed) && //diagonal corner trap !( @@ -1109,6 +1099,7 @@ public class HierarchyPathFinder implements Runnable{ } //TODO raycast spam = extremely slow + //...flowfield integration spam is also really slow. if(!(current == null || (costId == costGround && current.dangerous() && !tileOn.dangerous()))){ //when anyNearSolid is false, no solid tiles have been encountered anywhere so far, so raycasting is a waste of time From 5e22b093e65095c1a791e95f63eabd5e6e757ccb Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 17 Apr 2024 23:13:10 -0400 Subject: [PATCH 113/348] more bugfixes --- .../src/mindustry/ai/HierarchyPathFinder.java | 37 ++++++++++++------- .../mindustry/entities/comp/HitboxComp.java | 2 +- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index de066116b8..3479f94c57 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -71,8 +71,9 @@ public class HierarchyPathFinder implements Runnable{ //TODO: very dangerous usage; //TODO - it is accessed from the main thread //TODO - it is written to on the pathfinding thread - //maps position in world in (x + y * width format) to a cache of flow fields - IntMap fields = new IntMap<>(); + //TODO - it does not include + //maps position in world in (x + y * width format) | type (bitpacked to long) to a cache of flow fields + LongMap fields = new LongMap<>(); //MAIN THREAD ONLY Seq fieldList = new Seq<>(false); @@ -128,22 +129,26 @@ public class HierarchyPathFinder implements Runnable{ static class FieldCache{ final PathCost cost; + final int costId; final int team; final int goalPos; //frontier for flow fields final IntQueue frontier = new IntQueue(); //maps cluster index to field weights; 0 means uninitialized final IntMap fields = new IntMap<>(); + final long mapKey; //main thread only! long lastUpdateId = state.updateId; //TODO: how are the nodes merged? CAN they be merged? - FieldCache(PathCost cost, int team, int goalPos){ + FieldCache(PathCost cost, int costId, int team, int goalPos){ this.cost = cost; this.team = team; this.goalPos = goalPos; + this.costId = costId; + this.mapKey = Pack.longInt(goalPos, costId); } } @@ -162,7 +167,7 @@ public class HierarchyPathFinder implements Runnable{ //TODO: can the pathfinding thread even see these? unitRequests = new ObjectMap<>(); - fields = new IntMap<>(); + fields = new LongMap<>(); fieldList = new Seq<>(false); clusters = new Cluster[256][][]; @@ -213,7 +218,7 @@ public class HierarchyPathFinder implements Runnable{ //skipped N update -> drop it if(field.lastUpdateId <= state.updateId - 30){ //make sure it's only modified on the main thread...? but what about calling get() on this thread?? - queue.post(() -> fields.remove(field.goalPos)); + queue.post(() -> fields.remove(field.mapKey)); Core.app.post(() -> fieldList.remove(field)); } } @@ -222,7 +227,7 @@ public class HierarchyPathFinder implements Runnable{ if(debug){ Events.run(Trigger.draw, () -> { int team = player.team().id; - int cost = costGround; + int cost = 0; Draw.draw(Layer.overlayUI, () -> { Lines.stroke(1f); @@ -953,13 +958,14 @@ public class HierarchyPathFinder implements Runnable{ var nodePath = clusterAstar(request, costId, node, dest); - FieldCache cache = fields.get(goalPos); + FieldCache cache = fields.get(Pack.longInt(goalPos, costId)); //if true, extra values are added on the sides of existing field cells that face new cells. boolean addingFrontier = true; //create the cache if it doesn't exist, and initialize it if(cache == null){ - fields.put(goalPos, cache = new FieldCache(pcost, team, goalPos)); + cache = new FieldCache(pcost, costId, team, goalPos); + fields.put(cache.mapKey, cache); FieldCache fcache = cache; //register field in main thread for iteration Core.app.post(() -> fieldList.add(fcache)); @@ -1038,13 +1044,15 @@ public class HierarchyPathFinder implements Runnable{ boolean any = false; + long fieldKey = Pack.longInt(destPos, costId); + //use existing request if it exists. if(request != null && request.destination == destPos){ request.lastUpdateId = state.updateId; Tile tileOn = unit.tileOn(), initialTileOn = tileOn; //TODO: should fields be accessible from this thread? - FieldCache fieldCache = fields.get(destPos); + FieldCache fieldCache = fields.get(fieldKey); if(fieldCache != null && tileOn != null){ FieldCache old = request.oldCache; @@ -1059,7 +1067,7 @@ public class HierarchyPathFinder implements Runnable{ boolean recalc = false; unit.hitboxTile(Tmp.r3); //tile rect size has tile size factored in, since the ray cannot have thickness - float tileRectSize = tilesize + Tmp.r3.height - 0.1f; + float tileRectSize = tilesize + Tmp.r3.height; //TODO last pos can change if the flowfield changes. if(initialTileOn.pos() != request.lastTile || request.lastTargetTile == null){ @@ -1311,7 +1319,8 @@ public class HierarchyPathFinder implements Runnable{ int index = cx + cy * cwidth; for(var req : threadPathRequests){ - var field = fields.get(req.destination); + long mapKey = Pack.longInt(req.destination, pathCost); + var field = fields.get(mapKey); if((field != null && field.fields.containsKey(index)) || req.notFound){ invalidRequests.add(req); } @@ -1396,14 +1405,16 @@ public class HierarchyPathFinder implements Runnable{ continue; } - var field = fields.get(request.destination); + long mapKey = Pack.longInt(request.destination, request.costId); + + var field = fields.get(mapKey); if(field != null){ //it's only worth recalculating a path when the current frontier has finished; otherwise the unit will be following something incomplete. if(field.frontier.isEmpty()){ //remove the field, to be recalculated next update one recalculatePath is processed - fields.remove(field.goalPos); + fields.remove(field.mapKey); Core.app.post(() -> fieldList.remove(field)); //once the field is invalidated, make sure that all the requests that have it stored in their 'old' field, so units don't stutter during recalculations diff --git a/core/src/mindustry/entities/comp/HitboxComp.java b/core/src/mindustry/entities/comp/HitboxComp.java index a444f350ab..178ab1c228 100644 --- a/core/src/mindustry/entities/comp/HitboxComp.java +++ b/core/src/mindustry/entities/comp/HitboxComp.java @@ -68,7 +68,7 @@ abstract class HitboxComp implements Posc, Sized, QuadTreeObject{ public void hitboxTile(Rect rect){ //tile hitboxes are never bigger than a tile, otherwise units get stuck - float size = Math.min(hitSize * 0.66f, 7.9f); + float size = Math.min(hitSize * 0.66f, 7.8f); //TODO: better / more accurate version is //float size = hitSize * 0.85f; //- for tanks? From 243cc1e52777241fa10e6715a43aaaf682533172 Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 17 Apr 2024 23:28:01 -0400 Subject: [PATCH 114/348] Unit selection overlay improvements --- core/src/mindustry/graphics/OverlayRenderer.java | 10 +++++++++- core/src/mindustry/type/UnitType.java | 5 +++++ gradle.properties | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/graphics/OverlayRenderer.java b/core/src/mindustry/graphics/OverlayRenderer.java index 853304e7b2..14e9927e9c 100644 --- a/core/src/mindustry/graphics/OverlayRenderer.java +++ b/core/src/mindustry/graphics/OverlayRenderer.java @@ -131,7 +131,9 @@ public class OverlayRenderer{ Building build = (select instanceof BlockUnitc b ? b.tile() : select instanceof Building b ? b : null); TextureRegion region = build != null ? build.block.fullIcon : select instanceof Unit u ? u.icon() : Core.atlas.white(); - Draw.rect(region, select.getX(), select.getY(), select instanceof Unit u && !(select instanceof BlockUnitc) ? u.rotation - 90f : 0f); + if(!(select instanceof Unitc)){ + Draw.rect(region, select.getX(), select.getY()); + } for(int i = 0; i < 4; i++){ float rot = i * 90f + 45f + (-Time.time) % 360f; @@ -255,6 +257,12 @@ public class OverlayRenderer{ } } + public void checkApplySelection(Unit u){ + if(unitFade > 0 && lastSelect == u){ + Draw.mixcol(Pal.accent, unitFade); + } + } + private static class CoreEdge{ float x1, y1, x2, y2; Team t1, t2; diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index b9f7ecd089..04ca534adc 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -1465,6 +1465,7 @@ public class UnitType extends UnlockableContent implements Senseable{ } public void drawTank(T unit){ + applyColor(unit); Draw.rect(treadRegion, unit.x, unit.y, unit.rotation - 90); if(treadRegion.found()){ @@ -1636,6 +1637,10 @@ public class UnitType extends UnlockableContent implements Senseable{ if(unit.drownTime > 0 && unit.lastDrownFloor != null){ Draw.mixcol(Tmp.c1.set(unit.lastDrownFloor.mapColor).mul(0.83f), unit.drownTime * 0.9f); } + //this is horribly scuffed. + if(renderer != null && renderer.overlays != null){ + renderer.overlays.checkApplySelection(unit); + } } //endregion diff --git a/gradle.properties b/gradle.properties index 25be0e37a2..025014ab41 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=8a2decd656 +archash=e812c7a008 From 7465445b0bbbcf174760c54eb113bd1b9dfa7f7c Mon Sep 17 00:00:00 2001 From: Cragent <117364120+Baldur404@users.noreply.github.com> Date: Thu, 18 Apr 2024 21:17:14 +0800 Subject: [PATCH 115/348] Update servers_v7.json (#9753) update a server addr --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 1fcaf20e7a..b44f5d74f4 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -190,7 +190,7 @@ }, { "name": "Anana", - "address": ["mdtleague.top"] + "address": ["mdt.mdtleague.top"] }, { "name": "Vortex", From 604e732edb96b0bcf8dec056062a871d61deea88 Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 18 Apr 2024 10:05:21 -0400 Subject: [PATCH 116/348] Switched to unit UI icons for minimap/players --- core/src/mindustry/entities/comp/BlockUnitComp.java | 6 ++++++ core/src/mindustry/entities/comp/PlayerComp.java | 4 ++-- core/src/mindustry/entities/comp/UnitComp.java | 5 +++++ core/src/mindustry/graphics/MinimapRenderer.java | 2 +- core/src/mindustry/graphics/OverlayRenderer.java | 2 +- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/core/src/mindustry/entities/comp/BlockUnitComp.java b/core/src/mindustry/entities/comp/BlockUnitComp.java index 2ce9a3afd1..bd13c88991 100644 --- a/core/src/mindustry/entities/comp/BlockUnitComp.java +++ b/core/src/mindustry/entities/comp/BlockUnitComp.java @@ -43,6 +43,12 @@ abstract class BlockUnitComp implements Unitc{ return tile.block.fullIcon; } + @Replace + @Override + public TextureRegion uiIcon(){ + return tile.block.uiIcon; + } + @Override public void killed(){ tile.kill(); diff --git a/core/src/mindustry/entities/comp/PlayerComp.java b/core/src/mindustry/entities/comp/PlayerComp.java index fe787a8336..45a80b7c1f 100644 --- a/core/src/mindustry/entities/comp/PlayerComp.java +++ b/core/src/mindustry/entities/comp/PlayerComp.java @@ -72,9 +72,9 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra public TextureRegion icon(){ //display default icon for dead players - if(dead()) return core() == null ? UnitTypes.alpha.fullIcon : ((CoreBlock)bestCore().block).unitType.fullIcon; + if(dead()) return core() == null ? UnitTypes.alpha.uiIcon : ((CoreBlock)bestCore().block).unitType.uiIcon; - return unit.icon(); + return unit.uiIcon(); } public boolean displayAmmo(){ diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index 8ca3361cdd..c33e020693 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -671,6 +671,11 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I return type.fullIcon; } + /** @return a preview UI icon for this unit. */ + public TextureRegion uiIcon(){ + return type.uiIcon; + } + /** Actually destroys the unit, removing it and creating explosions. **/ public void destroy(){ if(!isAdded() || !type.killable) return; diff --git a/core/src/mindustry/graphics/MinimapRenderer.java b/core/src/mindustry/graphics/MinimapRenderer.java index e5f42b2c9a..a14935a379 100644 --- a/core/src/mindustry/graphics/MinimapRenderer.java +++ b/core/src/mindustry/graphics/MinimapRenderer.java @@ -166,7 +166,7 @@ public class MinimapRenderer{ if(unit.inFogTo(player.team()) || !unit.type.drawMinimap) continue; float scale = Scl.scl(1f) * tilesize * 3; - var region = unit.icon(); + var region = unit.uiIcon(); Draw.mixcol(unit.team.color, 1f); Draw.rect(region, unit.x, unit.y, scale, scale * (float)region.height / region.width, unit.rotation() - 90); diff --git a/core/src/mindustry/graphics/OverlayRenderer.java b/core/src/mindustry/graphics/OverlayRenderer.java index 14e9927e9c..844a087c8e 100644 --- a/core/src/mindustry/graphics/OverlayRenderer.java +++ b/core/src/mindustry/graphics/OverlayRenderer.java @@ -129,7 +129,7 @@ public class OverlayRenderer{ Draw.mixcol(Pal.accent, 1f); Draw.alpha(unitFade); Building build = (select instanceof BlockUnitc b ? b.tile() : select instanceof Building b ? b : null); - TextureRegion region = build != null ? build.block.fullIcon : select instanceof Unit u ? u.icon() : Core.atlas.white(); + TextureRegion region = build != null ? build.block.fullIcon : Core.atlas.white(); if(!(select instanceof Unitc)){ Draw.rect(region, select.getX(), select.getY()); From b35c435ebdef271050d9b2cc47c6593ed499aa4b Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 18 Apr 2024 11:15:15 -0400 Subject: [PATCH 117/348] Full unit payload drawing --- core/src/mindustry/type/UnitType.java | 4 ++++ .../world/blocks/payloads/UnitPayload.java | 17 ++++------------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index 04ca534adc..fbd7e599d7 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -431,6 +431,8 @@ public class UnitType extends UnlockableContent implements Senseable{ public TextureRegion[] wreckRegions, segmentRegions, segmentOutlineRegions; public TextureRegion[][] treadRegions; + //INTERNAL REQUIREMENTS + protected float buildTime = -1f; protected @Nullable ItemStack[] totalRequirements, cachedRequirements, firstRequirements; @@ -1183,6 +1185,8 @@ public class UnitType extends UnlockableContent implements Senseable{ //region drawing + + /** Set up drawing state before calling! e.g. drawingPayload */ public void draw(Unit unit){ if(unit.inFogTo(Vars.player.team())) return; diff --git a/core/src/mindustry/world/blocks/payloads/UnitPayload.java b/core/src/mindustry/world/blocks/payloads/UnitPayload.java index 48fd9cac8c..fb22ceb94c 100644 --- a/core/src/mindustry/world/blocks/payloads/UnitPayload.java +++ b/core/src/mindustry/world/blocks/payloads/UnitPayload.java @@ -146,19 +146,10 @@ public class UnitPayload implements Payload{ //TODO should not happen if(unit.type == null) return; - //TODO this would be more accurate but has all sorts of associated problems (?) - if(false){ - float e = unit.elevation; - unit.elevation = 0f; - //avoids drawing mining or building - unit.type.draw(unit); - unit.elevation = e; - return; - } - - unit.type.drawSoftShadow(unit); - Draw.rect(unit.type.fullIcon, unit.x, unit.y, unit.rotation - 90); - unit.type.drawCell(unit); + float e = unit.elevation; + unit.elevation = 0f; + unit.type.draw(unit); + unit.elevation = e; //draw warning if(overlayTime > 0){ From 1ac1263aa4b3bba6a0a2b3aa677cb6a2a1c58704 Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 18 Apr 2024 12:31:53 -0400 Subject: [PATCH 118/348] Unit avoiding re-enabled --- .../src/mindustry/ai/HierarchyPathFinder.java | 21 +++++++------------ core/src/mindustry/ai/types/CommandAI.java | 5 ++--- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java index 3479f94c57..7d43a2bdcd 100644 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ b/core/src/mindustry/ai/HierarchyPathFinder.java @@ -1021,13 +1021,17 @@ public class HierarchyPathFinder implements Runnable{ PathRequest request = unitRequests.get(unit); + unit.hitboxTile(Tmp.r3); + //tile rect size has tile size factored in, since the ray cannot have thickness + float tileRectSize = tilesize + Tmp.r3.height; + int lastRaycastTile = request == null || world.tileChanges != request.lastWorldUpdate ? -1 : request.lastRaycastTile; boolean raycastResult = request != null && request.lastRaycastResult; //cache raycast results to run every time the world updates, and every tile the unit crosses if(lastRaycastTile != packedPos){ //near the destination, standard raycasting tends to break down, so use the more permissive 'near' variant that doesn't take into account edges of walls - raycastResult = unit.within(destination, tilesize * 2.5f) ? !raycastNear(team, cost, tileX, tileY, actualDestX, actualDestY) : !raycast(team, cost, tileX, tileY, actualDestX, actualDestY); + raycastResult = unit.within(destination, tilesize * 2.5f) ? !raycastRect(unit.x, unit.y, destination.x, destination.y, team, cost, tileX, tileY, actualDestX, actualDestY, tileRectSize) : !raycast(team, cost, tileX, tileY, actualDestX, actualDestY); if(request != null){ request.lastRaycastTile = packedPos; @@ -1065,9 +1069,6 @@ public class HierarchyPathFinder implements Runnable{ int maxIterations = 30; //TODO higher/lower number? is this still too slow? int i = 0; boolean recalc = false; - unit.hitboxTile(Tmp.r3); - //tile rect size has tile size factored in, since the ray cannot have thickness - float tileRectSize = tilesize + Tmp.r3.height; //TODO last pos can change if the flowfield changes. if(initialTileOn.pos() != request.lastTile || request.lastTargetTile == null){ @@ -1079,8 +1080,8 @@ public class HierarchyPathFinder implements Runnable{ Tile current = null; int minCost = 0; - for(int dir = 0; dir < 8; dir ++){ - Point2 point = Geometry.d8[dir]; + for(int dir = 0; dir < 4; dir ++){ + Point2 point = Geometry.d4[dir]; int dx = tileOn.x + point.x, dy = tileOn.y + point.y; Tile other = world.tile(dx, dy); @@ -1094,13 +1095,7 @@ public class HierarchyPathFinder implements Runnable{ anyNearSolid = true; } - if((value == 0 || otherCost < value) && otherCost != impassable && (otherCost != 0 || packed == destPos) && (current == null || otherCost < minCost) && passable(unit.team.id, cost, packed) && - //diagonal corner trap - !( - (!passable(team, cost, world.packArray(tileOn.x + point.x, tileOn.y)) || - (!passable(team, cost, world.packArray(tileOn.x, tileOn.y + point.y)))) - ) - ){ + if((value == 0 || otherCost < value) && otherCost != impassable && (otherCost != 0 || packed == destPos) && (current == null || otherCost < minCost) && passable(unit.team.id, cost, packed)){ current = other; minCost = otherCost; } diff --git a/core/src/mindustry/ai/types/CommandAI.java b/core/src/mindustry/ai/types/CommandAI.java index 042852e05f..07a30878b1 100644 --- a/core/src/mindustry/ai/types/CommandAI.java +++ b/core/src/mindustry/ai/types/CommandAI.java @@ -223,8 +223,8 @@ public class CommandAI extends AIController{ } if(unit.isGrounded() && stance != UnitStance.ram){ - //TODO: blocking is disabled, doesn't work well - if(timer.get(timerTarget3, avoidInterval) && false){ + //TODO: blocking enable or disable? + if(timer.get(timerTarget3, avoidInterval)){ Vec2 dstPos = Tmp.v1.trns(unit.rotation, unit.hitSize/2f); float max = unit.hitSize/2f; float radius = Math.max(7f, max); @@ -251,7 +251,6 @@ public class CommandAI extends AIController{ timeSpentBlocked = 0f; } - //if you've spent 3 seconds stuck, something is wrong, move regardless move = hpath.getPathPosition(unit, vecMovePos, targetPos, vecOut, noFound) && (!blockingUnit || timeSpentBlocked > maxBlockTime); //rare case where unit must be perfectly aligned (happens with 1-tile gaps) alwaysArrive = vecOut.epsilonEquals(unit.tileX() * tilesize, unit.tileY() * tilesize); From 3a7d7647d98ea5e53eba2f64440b8e713d4789ac Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 18 Apr 2024 12:48:04 -0400 Subject: [PATCH 119/348] HPA* merged in --- core/src/mindustry/Vars.java | 2 - core/src/mindustry/ai/ControlPathfinder.java | 1840 ++++++++++++----- .../src/mindustry/ai/HierarchyPathFinder.java | 1471 ------------- core/src/mindustry/ai/types/CommandAI.java | 6 +- core/src/mindustry/ai/types/LogicAI.java | 2 +- 5 files changed, 1331 insertions(+), 1990 deletions(-) delete mode 100644 core/src/mindustry/ai/HierarchyPathFinder.java diff --git a/core/src/mindustry/Vars.java b/core/src/mindustry/Vars.java index 6d4006b074..552f0ac54f 100644 --- a/core/src/mindustry/Vars.java +++ b/core/src/mindustry/Vars.java @@ -241,7 +241,6 @@ public class Vars implements Loadable{ public static WaveSpawner spawner; public static BlockIndexer indexer; public static Pathfinder pathfinder; - public static HierarchyPathFinder hpath; public static ControlPathfinder controlPath; public static FogControl fogControl; @@ -315,7 +314,6 @@ public class Vars implements Loadable{ indexer = new BlockIndexer(); pathfinder = new Pathfinder(); controlPath = new ControlPathfinder(); - hpath = new HierarchyPathFinder(); fogControl = new FogControl(); bases = new BaseRegistry(); logicVars = new GlobalVars(); diff --git a/core/src/mindustry/ai/ControlPathfinder.java b/core/src/mindustry/ai/ControlPathfinder.java index f6b166270e..f50345a554 100644 --- a/core/src/mindustry/ai/ControlPathfinder.java +++ b/core/src/mindustry/ai/ControlPathfinder.java @@ -7,6 +7,8 @@ import arc.math.*; import arc.math.geom.*; import arc.struct.*; import arc.util.*; +import mindustry.annotations.Annotations.*; +import mindustry.content.*; import mindustry.core.*; import mindustry.game.EventType.*; import mindustry.game.*; @@ -17,14 +19,13 @@ import mindustry.world.*; import static mindustry.Vars.*; import static mindustry.ai.Pathfinder.*; -//TODO remove/replace -public class ControlPathfinder{ - //TODO this FPS-based update system could be flawed. - private static final long maxUpdate = Time.millisToNanos(30); - private static final int updateFPS = 60; - private static final int updateInterval = 1000 / updateFPS; +//https://webdocs.cs.ualberta.ca/~mmueller/ps/hpastar.pdf +//https://www.gameaipro.com/GameAIPro/GameAIPro_Chapter23_Crowd_Pathfinding_and_Steering_Using_Flow_Field_Tiles.pdf +public class ControlPathfinder implements Runnable{ private static final int wallImpassableCap = 1_000_000; + public static boolean showDebug; + public static final PathCost costGround = (team, tile) -> @@ -75,295 +76,1188 @@ public class ControlPathfinder{ costNaval ); - public static boolean showDebug = false; + private static final long maxUpdate = Time.millisToNanos(12); + private static final int updateStepInterval = 200; + private static final int updateFPS = 30; + private static final int updateInterval = 1000 / updateFPS, invalidateCheckInterval = 1000; - //static access probably faster than object access - static int wwidth, wheight; - //increments each tile change - static volatile int worldUpdateId; + static final int clusterSize = 12; - /** Current pathfinding threads, contents may be null */ - @Nullable PathfindThread[] threads; - /** for unique target IDs */ - int lastTargetId = 1; - /** requests per-unit */ - ObjectMap requests = new ObjectMap<>(); + static final int[] offsets = { + 1, 0, //right: bottom to top + 0, 1, //top: left to right + 0, 0, //left: bottom to top + 0, 0 //bottom: left to right + }; + + static final int[] moveDirs = { + 0, 1, + 1, 0, + 0, 1, + 1, 0 + }; + + static final int[] nextOffsets = { + 1, 0, + 0, 1, + -1, 0, + 0, -1 + }; + + //maps team -> pathCost -> flattened array of clusters in 2D + //(what about teams? different path costs?) + Cluster[][][] clusters; + + int cwidth, cheight; + + //temporarily used for resolving connections for intra-edges + IntSet usedEdges = new IntSet(); + //tasks to run on pathfinding thread + TaskQueue queue = new TaskQueue(); + + //individual requests based on unit - MAIN THREAD ONLY + ObjectMap unitRequests = new ObjectMap<>(); + + Seq threadPathRequests = new Seq<>(false); + + //TODO: very dangerous usage; + //TODO - it is accessed from the main thread + //TODO - it is written to on the pathfinding thread + //maps position in world in (x + y * width format) | type (bitpacked to long) to a cache of flow fields + LongMap fields = new LongMap<>(); + //MAIN THREAD ONLY + Seq fieldList = new Seq<>(false); + + //these are for inner edge A* (temporary!) + IntFloatMap innerCosts = new IntFloatMap(); + PathfindQueue innerFrontier = new PathfindQueue(); + + //ONLY modify on pathfinding thread. + IntSet clustersToUpdate = new IntSet(); + IntSet clustersToInnerUpdate = new IntSet(); + + //PATHFINDING THREAD - requests that should be recomputed + ObjectSet invalidRequests = new ObjectSet<>(); + + /** Current pathfinding thread */ + @Nullable Thread thread; + + //path requests are per-unit + static class PathRequest{ + final Unit unit; + final int destination, team, costId; + //resulting path of nodes + final IntSeq resultPath = new IntSeq(); + + //node index -> total cost + @Nullable IntFloatMap costs = new IntFloatMap(); + //node index (NodeIndex struct) -> node it came from TODO merge them, make properties of FieldCache? + @Nullable IntIntMap cameFrom = new IntIntMap(); + //frontier for A* + @Nullable PathfindQueue frontier = new PathfindQueue(); + + //main thread only! + long lastUpdateId = state.updateId; + + //both threads + volatile boolean notFound = false; + volatile boolean invalidated = false; + //old field assigned before everything was recomputed + @Nullable volatile FieldCache oldCache; + + boolean lastRaycastResult = false; + int lastRaycastTile, lastWorldUpdate; + int lastTile; + @Nullable Tile lastTargetTile; + + PathRequest(Unit unit, int team, int costId, int destination){ + this.unit = unit; + this.costId = costId; + this.team = team; + this.destination = destination; + } + } + + static class FieldCache{ + final PathCost cost; + final int costId; + final int team; + final int goalPos; + //frontier for flow fields + final IntQueue frontier = new IntQueue(); + //maps cluster index to field weights; 0 means uninitialized + final IntMap fields = new IntMap<>(); + final long mapKey; + + //main thread only! + long lastUpdateId = state.updateId; + + //TODO: how are the nodes merged? CAN they be merged? + + FieldCache(PathCost cost, int costId, int team, int goalPos){ + this.cost = cost; + this.team = team; + this.goalPos = goalPos; + this.costId = costId; + this.mapKey = Pack.longInt(goalPos, costId); + } + } + + static class Cluster{ + IntSeq[] portals = new IntSeq[4]; + //maps rotation + index of portal to list of IntraEdge objects + LongSeq[][] portalConnections = new LongSeq[4][]; + } public ControlPathfinder(){ + Events.on(ResetEvent.class, event -> stop()); + Events.on(WorldLoadEvent.class, event -> { stop(); - wwidth = world.width(); - wheight = world.height(); + + //TODO: can the pathfinding thread even see these? + unitRequests = new ObjectMap<>(); + fields = new LongMap<>(); + fieldList = new Seq<>(false); + + clusters = new Cluster[256][][]; + cwidth = Mathf.ceil((float)world.width() / clusterSize); + cheight = Mathf.ceil((float)world.height() / clusterSize); + start(); }); - //only update the world when a solid block is removed or placed, everything else doesn't matter - Events.on(TilePreChangeEvent.class, e -> { - if(e.tile.solid()){ - worldUpdateId ++; - } - }); - Events.on(TileChangeEvent.class, e -> { - if(e.tile.solid()){ - worldUpdateId ++; - } - }); - Events.on(ResetEvent.class, event -> stop()); + e.tile.getLinkedTiles(t -> { + int x = t.x, y = t.y, mx = x % clusterSize, my = y % clusterSize, cx = x / clusterSize, cy = y / clusterSize, cluster = cx + cy * cwidth; + + //is at the edge of a cluster; this means the portals may have changed. + if(mx == 0 || my == 0 || mx == clusterSize - 1 || my == clusterSize - 1){ + + if(mx == 0) queueClusterUpdate(cx - 1, cy); //left + if(my == 0) queueClusterUpdate(cx, cy - 1); //bottom + if(mx == clusterSize - 1) queueClusterUpdate(cx + 1, cy); //right + if(my == clusterSize - 1) queueClusterUpdate(cx, cy + 1); //top + + queueClusterUpdate(cx, cy); + //TODO: recompute edge clusters too. + }else{ + //there is no need to recompute portals for block updates that are not on the edge. + queue.post(() -> clustersToInnerUpdate.add(cluster)); + } + }); + + //TODO: recalculate affected flow fields? or just all of them? how to reflow? + }); //invalidate paths Events.run(Trigger.update, () -> { - for(var req : requests.values()){ + for(var req : unitRequests.values()){ //skipped N update -> drop it if(req.lastUpdateId <= state.updateId - 10){ + req.invalidated = true; //concurrent modification! - Core.app.post(() -> requests.remove(req.unit)); - req.thread.queue.post(() -> req.thread.requests.remove(req)); + queue.post(() -> threadPathRequests.remove(req)); + Core.app.post(() -> unitRequests.remove(req.unit)); + } + } + + for(var field : fieldList){ + //skipped N update -> drop it + if(field.lastUpdateId <= state.updateId - 30){ + //make sure it's only modified on the main thread...? but what about calling get() on this thread?? + queue.post(() -> fields.remove(field.mapKey)); + Core.app.post(() -> fieldList.remove(field)); } } }); - Events.run(Trigger.draw, () -> { - if(!showDebug) return; + if(showDebug){ + Events.run(Trigger.draw, () -> { + int team = player.team().id; + int cost = 0; - for(var req : requests.values()){ - if(req.frontier == null) continue; Draw.draw(Layer.overlayUI, () -> { - if(req.done){ - int len = req.result.size; - int rp = req.rayPathIndex; - if(rp < len && rp >= 0){ - Draw.color(Color.royal); - Tile tile = tile(req.result.items[rp]); - Lines.line(req.unit.x, req.unit.y, tile.worldx(), tile.worldy()); - } + Lines.stroke(1f); - for(int i = 0; i < len; i++){ - Draw.color(Tmp.c1.set(Color.white).fromHsv(i / (float)len * 360f, 1f, 0.9f)); - int pos = req.result.items[i]; - Fill.square(pos % wwidth * tilesize, pos / wwidth * tilesize, 3f); + if(clusters[team] != null && clusters[team][cost] != null){ + for(int cx = 0; cx < cwidth; cx++){ + for(int cy = 0; cy < cheight; cy++){ - if(i == req.pathIndex){ - Draw.color(Color.green); - Lines.square(pos % wwidth * tilesize, pos / wwidth * tilesize, 5f); - } - } - }else{ - var view = Core.camera.bounds(Tmp.r1); - int len = req.frontier.size; - float[] weights = req.frontier.weights; - int[] poses = req.frontier.queue; - for(int i = 0; i < Math.min(len, 1000); i++){ - int pos = poses[i]; - if(view.contains(pos % wwidth * tilesize, pos / wwidth * tilesize)){ - Draw.color(Tmp.c1.set(Color.white).fromHsv((weights[i] * 4f) % 360f, 1f, 0.9f)); + var cluster = clusters[team][cost][cy * cwidth + cx]; + if(cluster != null){ + Lines.stroke(0.5f); + Draw.color(Color.gray); + Lines.stroke(1f); - Lines.square(pos % wwidth * tilesize, pos / wwidth * tilesize, 4f); + Lines.rect(cx * clusterSize * tilesize - tilesize/2f, cy * clusterSize * tilesize - tilesize/2f, clusterSize * tilesize, clusterSize * tilesize); + + + for(int d = 0; d < 4; d++){ + IntSeq portals = cluster.portals[d]; + if(portals != null){ + + for(int i = 0; i < portals.size; i++){ + int pos = portals.items[i]; + int from = Point2.x(pos), to = Point2.y(pos); + float width = tilesize * (Math.abs(from - to) + 1), height = tilesize; + + portalToVec(cluster, cx, cy, d, i, Tmp.v1); + + Draw.color(Color.brown); + Lines.ellipse(30, Tmp.v1.x, Tmp.v1.y, width / 2f, height / 2f, d * 90f - 90f); + + LongSeq connections = cluster.portalConnections[d] == null ? null : cluster.portalConnections[d][i]; + + if(connections != null){ + Draw.color(Color.forest); + for(int coni = 0; coni < connections.size; coni ++){ + long con = connections.items[coni]; + + portalToVec(cluster, cx, cy, IntraEdge.dir(con), IntraEdge.portal(con), Tmp.v2); + + float + x1 = Tmp.v1.x, y1 = Tmp.v1.y, + x2 = Tmp.v2.x, y2 = Tmp.v2.y; + Lines.line(x1, y1, x2, y2); + + } + } + } + } + } + } } } } - Draw.reset(); + + for(var fields : fieldList){ + try{ + for(var entry : fields.fields){ + int cx = entry.key % cwidth, cy = entry.key / cwidth; + for(int y = 0; y < clusterSize; y++){ + for(int x = 0; x < clusterSize; x++){ + int value = entry.value[x + y * clusterSize]; + Tmp.c1.a = 1f; + Lines.stroke(0.8f, Tmp.c1.fromHsv(value * 3f, 1f, 1f)); + Draw.alpha(0.5f); + Fill.square((x + cx * clusterSize) * tilesize, (y + cy * clusterSize) * tilesize, tilesize / 2f); + } + } + } + }catch(Exception ignored){} //probably has some concurrency issues when iterating but I don't care, this is for debugging + } }); - } - }); + + Draw.reset(); + }); + } } - - /** @return the next target ID to use as a unique path identifier. */ - public int nextTargetId(){ - return lastTargetId ++; + void queueClusterUpdate(int cx, int cy){ + if(cx >= 0 && cy >= 0 && cx < cwidth && cy < cheight){ + queue.post(() -> clustersToUpdate.add(cx + cy * cwidth)); + } } - /** - * @return whether a path is ready. - * @param pathId a unique ID for this location query, which should change every time the 'destination' vector is modified. - * */ - public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 out){ - return getPathPosition(unit, pathId, destination, out, null); + //debugging only! + void portalToVec(Cluster cluster, int cx, int cy, int direction, int portalIndex, Vec2 out){ + int pos = cluster.portals[direction].items[portalIndex]; + int from = Point2.x(pos), to = Point2.y(pos); + int addX = moveDirs[direction * 2], addY = moveDirs[direction * 2 + 1]; + float average = (from + to) / 2f; + + float + x = (addX * average + cx * clusterSize + offsets[direction * 2] * (clusterSize - 1) + nextOffsets[direction * 2] / 2f) * tilesize, + y = (addY * average + cy * clusterSize + offsets[direction * 2 + 1] * (clusterSize - 1) + nextOffsets[direction * 2 + 1] / 2f) * tilesize; + + out.set(x, y); } - /** - * @return whether a path is ready. - * @param pathId a unique ID for this location query, which should change every time the 'destination' vector is modified. - * @param noResultFound extra return value for storing whether no valid path to the destination exists (thanks java!) - * */ - public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 out, @Nullable boolean[] noResultFound){ - if(noResultFound != null){ - noResultFound[0] = false; - } - - //uninitialized - if(threads == null || !world.tiles.in(World.toTile(destination.x), World.toTile(destination.y))) return false; - - PathCost costType = unit.type.pathCost; - int team = unit.team.id; - - //if the destination can be trivially reached in a straight line, do that. - if((!requests.containsKey(unit) || requests.get(unit).curId != pathId) && !raycast(team, costType, unit.tileX(), unit.tileY(), World.toTile(destination.x), World.toTile(destination.y))){ - out.set(destination); - return true; - } - - //destination is impassable, can't go there. - if(solid(team, costType, world.packArray(World.toTile(destination.x), World.toTile(destination.y)), false)){ - return false; - } - - //check for request existence - if(!requests.containsKey(unit)){ - PathfindThread thread = Structs.findMin(threads, t -> t.requestSize); - - var req = new PathRequest(thread); - req.unit = unit; - req.cost = costType; - req.destination.set(destination); - req.curId = pathId; - req.team = team; - req.lastUpdateId = state.updateId; - req.lastPos.set(unit); - req.lastWorldUpdate = worldUpdateId; - //raycast immediately when done - req.raycastTimer = 9999f; - - requests.put(unit, req); - - //add to thread so it gets processed next update - thread.queue.post(() -> thread.requests.add(req)); - }else{ - var req = requests.get(unit); - req.lastUpdateId = state.updateId; - req.team = unit.team.id; - if(req.curId != req.lastId || req.curId != pathId){ - req.pathIndex = 0; - req.rayPathIndex = -1; - req.done = false; - req.foundEnd = false; - } - - req.destination.set(destination); - req.curId = pathId; - - //check for the unit getting stuck every N seconds - if(req.done && (req.stuckTimer += Time.delta) >= 60f * 1.5f){ - req.stuckTimer = 0f; - //force recalculate - if(req.lastPos.within(unit, 1.5f)){ - req.forceRecalculate(); - } - req.lastPos.set(unit); - } - - if(req.done){ - int[] items = req.result.items; - int len = req.result.size; - int tileX = unit.tileX(), tileY = unit.tileY(); - float range = 4f; - - float minDst = req.pathIndex < len ? unit.dst2(world.tiles.geti(items[req.pathIndex])) : 0f; - int idx = req.pathIndex; - - //find closest node that is in front of the path index and hittable with raycast - for(int i = len - 1; i >= idx; i--){ - Tile tile = tile(items[i]); - float dst = unit.dst2(tile); - //TODO maybe put this on a timer since raycasts can be expensive? - if(dst < minDst && !permissiveRaycast(team, costType, tileX, tileY, tile.x, tile.y)){ - if(avoid(req.team, req.cost, items[i + 1])){ - range = 0.5f; - } - - req.pathIndex = Math.max(dst <= range * range ? i + 1 : i, req.pathIndex); - minDst = Math.min(dst, minDst); - }else if(dst <= 1f){ - req.pathIndex = Math.min(Math.max(i + 1, req.pathIndex), len - 1); - } - } - - if(req.rayPathIndex < 0){ - req.rayPathIndex = req.pathIndex; - } - - if((req.raycastTimer += Time.delta) >= 50f){ - for(int i = len - 1; i > req.pathIndex; i--){ - int val = items[i]; - if(!raycast(team, costType, tileX, tileY, val % wwidth, val / wwidth)){ - req.rayPathIndex = i; - break; - } - } - req.raycastTimer = 0; - } - - if(req.rayPathIndex < len && req.rayPathIndex >= 0){ - Tile tile = tile(items[req.rayPathIndex]); - out.set(tile); - - if(req.rayPathIndex > 0){ - float angleToNext = tile(items[req.rayPathIndex - 1]).angleTo(tile); - float angleToDest = unit.angleTo(tile); - //force recalculate when the unit moves backwards - if(Angles.angleDist(angleToNext, angleToDest) > 80f && !unit.within(tile, 1f)){ - req.forceRecalculate(); - } - } - - if(avoid(req.team, req.cost, items[req.rayPathIndex])){ - range = 0.5f; - } - - if(unit.within(tile, range)){ - req.pathIndex = req.rayPathIndex = Math.max(req.pathIndex, req.rayPathIndex + 1); - } - }else{ - //implicit done - out.set(unit); - //end of path, we're done here? reset path? what??? - } - - if(noResultFound != null){ - noResultFound[0] = !req.foundEnd; - } - } - - return req.done; - } - - return false; - } /** Starts or restarts the pathfinding thread. */ private void start(){ stop(); - if(net.client()) return; - //TODO currently capped at 6 threads, might be a good idea to make it more? - threads = new PathfindThread[Mathf.clamp(Runtime.getRuntime().availableProcessors() - 1, 1, 6)]; - for(int i = 0; i < threads.length; i ++){ - threads[i] = new PathfindThread("ControlPathfindThread-" + i); - threads[i].setPriority(Thread.MIN_PRIORITY); - threads[i].setDaemon(true); - threads[i].start(); - } + thread = new Thread(this, "Control Pathfinder"); + thread.setPriority(Thread.MIN_PRIORITY); + thread.setDaemon(true); + thread.start(); } /** Stops the pathfinding thread. */ private void stop(){ - if(threads != null){ - for(var thread : threads){ - thread.interrupt(); + if(thread != null){ + thread.interrupt(); + thread = null; + } + queue.clear(); + } + + /** @return a cluster at coordinates; can be null if not cluster was created yet*/ + @Nullable Cluster getCluster(int team, int pathCost, int cx, int cy){ + return getCluster(team, pathCost, cx + cy * cwidth); + } + + /** @return a cluster at coordinates; can be null if not cluster was created yet*/ + @Nullable Cluster getCluster(int team, int pathCost, int clusterIndex){ + if(clusters == null) return null; + + Cluster[][] dim1 = clusters[team]; + + if(dim1 == null) return null; + + Cluster[] dim2 = dim1[pathCost]; + + if(dim2 == null) return null; + + return dim2[clusterIndex]; + } + + /** @return the cluster at specified coordinates; never null. */ + Cluster getCreateCluster(int team, int pathCost, int cx, int cy){ + return getCreateCluster(team, pathCost, cx + cy * cwidth); + } + + /** @return the cluster at specified coordinates; never null. */ + Cluster getCreateCluster(int team, int pathCost, int clusterIndex){ + Cluster result = getCluster(team, pathCost, clusterIndex); + if(result == null){ + return updateCluster(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth); + }else{ + return result; + } + } + + Cluster updateCluster(int team, int pathCost, int cx, int cy){ + //TODO: what if clusters are null for thread visibility reasons? + + Cluster[][] dim1 = clusters[team]; + + if(dim1 == null){ + dim1 = clusters[team] = new Cluster[Team.all.length][]; + } + + Cluster[] dim2 = dim1[pathCost]; + + if(dim2 == null){ + dim2 = dim1[pathCost] = new Cluster[cwidth * cheight]; + } + + Cluster cluster = dim2[cy * cwidth + cx]; + if(cluster == null){ + cluster = dim2[cy * cwidth + cx] = new Cluster(); + }else{ + //reset data + for(var p : cluster.portals){ + p.clear(); } } - threads = null; - requests.clear(); + + PathCost cost = idToCost(pathCost); + + for(int direction = 0; direction < 4; direction++){ + int otherX = cx + Geometry.d4x(direction), otherY = cy + Geometry.d4y(direction); + //out of bounds, no portals in this direction + if(otherX < 0 || otherY < 0 || otherX >= cwidth || otherY >= cheight){ + continue; + } + + Cluster other = dim2[otherX + otherY * cwidth]; + IntSeq portals; + + if(other == null){ + //create new portals at direction + portals = cluster.portals[direction] = new IntSeq(4); + }else{ + //share portals with the other cluster + portals = cluster.portals[direction] = other.portals[(direction + 2) % 4]; + + //clear the portals, they're being recalculated now + portals.clear(); + } + + int addX = moveDirs[direction * 2], addY = moveDirs[direction * 2 + 1]; + int + baseX = cx * clusterSize + offsets[direction * 2] * (clusterSize - 1), + baseY = cy * clusterSize + offsets[direction * 2 + 1] * (clusterSize - 1), + nextBaseX = baseX + Geometry.d4[direction].x, + nextBaseY = baseY + Geometry.d4[direction].y; + + int lastPortal = -1; + boolean prevSolid = true; + + for(int i = 0; i < clusterSize; i++){ + int x = baseX + addX * i, y = baseY + addY * i; + + //scan for portals + if(solid(team, cost, x, y) || solid(team, cost, nextBaseX + addX * i, nextBaseY + addY * i)){ + int previous = i - 1; + //hit a wall, create portals between the two points + if(!prevSolid && previous >= lastPortal){ + //portals are an inclusive range + portals.add(Point2.pack(previous, lastPortal)); + } + prevSolid = true; + }else{ + //empty area encountered, mark the location of portal start + if(prevSolid){ + lastPortal = i; + } + prevSolid = false; + } + } + + //at the end of the loop, close any un-initialized portals; this is copy pasted code + int previous = clusterSize - 1; + if(!prevSolid && previous >= lastPortal){ + //portals are an inclusive range + portals.add(Point2.pack(previous, lastPortal)); + } + } + + updateInnerEdges(team, cost, cx, cy, cluster); + + return cluster; + } + + void updateInnerEdges(int team, int cost, int cx, int cy, Cluster cluster){ + updateInnerEdges(team, idToCost(cost), cx, cy, cluster); + } + + void updateInnerEdges(int team, PathCost cost, int cx, int cy, Cluster cluster){ + int minX = cx * clusterSize, minY = cy * clusterSize, maxX = Math.min(minX + clusterSize - 1, wwidth - 1), maxY = Math.min(minY + clusterSize - 1, wheight - 1); + + usedEdges.clear(); + + //clear all connections, since portals changed, they need to be recomputed. + cluster.portalConnections = new LongSeq[4][]; + + for(int direction = 0; direction < 4; direction++){ + var portals = cluster.portals[direction]; + if(portals == null) continue; + + int addX = moveDirs[direction * 2], addY = moveDirs[direction * 2 + 1]; + + for(int i = 0; i < portals.size; i++){ + usedEdges.add(Point2.pack(direction, i)); + + int + portal = portals.items[i], + from = Point2.x(portal), to = Point2.y(portal), + average = (from + to) / 2, + x = (addX * average + cx * clusterSize + offsets[direction * 2] * (clusterSize - 1)), + y = (addY * average + cy * clusterSize + offsets[direction * 2 + 1] * (clusterSize - 1)); + + for(int otherDir = 0; otherDir < 4; otherDir++){ + var otherPortals = cluster.portals[otherDir]; + if(otherPortals == null) continue; + + for(int j = 0; j < otherPortals.size; j++){ + + if(!usedEdges.contains(Point2.pack(otherDir, j))){ + + int + other = otherPortals.items[j], + otherFrom = Point2.x(other), otherTo = Point2.y(other), + otherAverage = (otherFrom + otherTo) / 2, + ox = cx * clusterSize + offsets[otherDir * 2] * (clusterSize - 1), + oy = cy * clusterSize + offsets[otherDir * 2 + 1] * (clusterSize - 1), + otherX = (moveDirs[otherDir * 2] * otherAverage + ox), + otherY = (moveDirs[otherDir * 2 + 1] * otherAverage + oy); + + //duplicate portal; should never happen. + if(Point2.pack(x, y) == Point2.pack(otherX, otherY)){ + continue; + } + + float connectionCost = innerAstar( + team, cost, + minX, minY, maxX, maxY, + x + y * wwidth, + otherX + otherY * wwidth, + (moveDirs[otherDir * 2] * otherFrom + ox), + (moveDirs[otherDir * 2 + 1] * otherFrom + oy), + (moveDirs[otherDir * 2] * otherTo + ox), + (moveDirs[otherDir * 2 + 1] * otherTo + oy) + ); + + if(connectionCost != -1f){ + if(cluster.portalConnections[direction] == null) cluster.portalConnections[direction] = new LongSeq[cluster.portals[direction].size]; + if(cluster.portalConnections[otherDir] == null) cluster.portalConnections[otherDir] = new LongSeq[cluster.portals[otherDir].size]; + if(cluster.portalConnections[direction][i] == null) cluster.portalConnections[direction][i] = new LongSeq(8); + if(cluster.portalConnections[otherDir][j] == null) cluster.portalConnections[otherDir][j] = new LongSeq(8); + + //TODO: can there be duplicate edges?? + cluster.portalConnections[direction][i].add(IntraEdge.get(otherDir, j, connectionCost)); + cluster.portalConnections[otherDir][j].add(IntraEdge.get(direction, i, connectionCost)); + } + } + } + } + } + } + } + + //distance heuristic: manhattan + private static float heuristic(int a, int b){ + int x = a % wwidth, x2 = b % wwidth, y = a / wwidth, y2 = b / wwidth; + return Math.abs(x - x2) + Math.abs(y - y2); + } + + private static int tcost(int team, PathCost cost, int tilePos){ + return cost.getCost(team, pathfinder.tiles[tilePos]); + } + + private static float tileCost(int team, PathCost type, int a, int b){ + //currently flat cost + return cost(team, type, b); + } + + /** @return -1 if no path was found */ + float innerAstar(int team, PathCost cost, int minX, int minY, int maxX, int maxY, int startPos, int goalPos, int goalX1, int goalY1, int goalX2, int goalY2){ + var frontier = innerFrontier; + var costs = innerCosts; + + frontier.clear(); + costs.clear(); + + //TODO: this can be faster and more memory efficient by making costs a NxN array... probably? + costs.put(startPos, 0); + frontier.add(startPos, 0); + + if(goalX2 < goalX1){ + int tmp = goalX1; + goalX1 = goalX2; + goalX2 = tmp; + } + + if(goalY2 < goalY1){ + int tmp = goalY1; + goalY1 = goalY2; + goalY2 = tmp; + } + + while(frontier.size > 0){ + int current = frontier.poll(); + + int cx = current % wwidth, cy = current / wwidth; + + //found the goal (it's in the portal rectangle) + if((cx >= goalX1 && cy >= goalY1 && cx <= goalX2 && cy <= goalY2) || current == goalPos){ + return costs.get(current); + } + + for(Point2 point : Geometry.d4){ + int newx = cx + point.x, newy = cy + point.y; + int next = newx + wwidth * newy; + + if(newx > maxX || newy > maxY || newx < minX || newy < minY || tcost(team, cost, next) == impassable) continue; + + float add = tileCost(team, cost, current, next); + + if(add < 0) continue; + + float newCost = costs.get(current) + add; + + if(newCost < costs.get(next, Float.POSITIVE_INFINITY)){ + costs.put(next, newCost); + float priority = newCost + heuristic(next, goalPos); + frontier.add(next, priority); + } + } + } + + return -1f; + } + + int makeNodeIndex(int cx, int cy, int dir, int portal){ + //to make sure there's only one way to refer to each node, the direction must be 0 or 1 (referring to portals on the top or right edge) + + //direction can only be 2 if cluster X is 0 (left edge of map) + if(dir == 2 && cx != 0){ + dir = 0; + cx --; + } + + //direction can only be 3 if cluster Y is 0 (bottom edge of map) + if(dir == 3 && cy != 0){ + dir = 1; + cy --; + } + + return NodeIndex.get(cx + cy * cwidth, dir, portal); + } + + //uses A* to find the closest node index to specified coordinates + //this node is used in cluster A* + /** @return MAX_VALUE if no node is found */ + private int findClosestNode(int team, int pathCost, int tileX, int tileY){ + int cx = tileX / clusterSize, cy = tileY / clusterSize; + + if(cx < 0 || cy < 0 || cx >= cwidth || cy >= cheight){ + return Integer.MAX_VALUE; + } + + PathCost cost = idToCost(pathCost); + Cluster cluster = getCreateCluster(team, pathCost, cx, cy); + int minX = cx * clusterSize, minY = cy * clusterSize, maxX = Math.min(minX + clusterSize - 1, wwidth - 1), maxY = Math.min(minY + clusterSize - 1, wheight - 1); + + int bestPortalPair = Integer.MAX_VALUE; + float bestCost = Float.MAX_VALUE; + + //A* to every node, find the best one (I know there's a better algorithm for this, probably dijkstra) + for(int dir = 0; dir < 4; dir++){ + var portals = cluster.portals[dir]; + if(portals == null) continue; + + for(int j = 0; j < portals.size; j++){ + + int + other = portals.items[j], + otherFrom = Point2.x(other), otherTo = Point2.y(other), + otherAverage = (otherFrom + otherTo) / 2, + ox = cx * clusterSize + offsets[dir * 2] * (clusterSize - 1), + oy = cy * clusterSize + offsets[dir * 2 + 1] * (clusterSize - 1), + otherX = (moveDirs[dir * 2] * otherAverage + ox), + otherY = (moveDirs[dir * 2 + 1] * otherAverage + oy); + + float connectionCost = innerAstar( + team, cost, + minX, minY, maxX, maxY, + tileX + tileY * wwidth, + otherX + otherY * wwidth, + (moveDirs[dir * 2] * otherFrom + ox), + (moveDirs[dir * 2 + 1] * otherFrom + oy), + (moveDirs[dir * 2] * otherTo + ox), + (moveDirs[dir * 2 + 1] * otherTo + oy) + ); + + //better cost found, update and return + if(connectionCost != -1f && connectionCost < bestCost){ + bestPortalPair = Point2.pack(dir, j); + bestCost = connectionCost; + } + } + } + + if(bestPortalPair != Integer.MAX_VALUE){ + return makeNodeIndex(cx, cy, Point2.x(bestPortalPair), Point2.y(bestPortalPair)); + } + + + return Integer.MAX_VALUE; + } + + //distance heuristic: manhattan + private float clusterNodeHeuristic(int team, int pathCost, int nodeA, int nodeB){ + int + clusterA = NodeIndex.cluster(nodeA), + dirA = NodeIndex.dir(nodeA), + portalA = NodeIndex.portal(nodeA), + clusterB = NodeIndex.cluster(nodeB), + dirB = NodeIndex.dir(nodeB), + portalB = NodeIndex.portal(nodeB), + rangeA = getCreateCluster(team, pathCost, clusterA).portals[dirA].items[portalA], + rangeB = getCreateCluster(team, pathCost, clusterB).portals[dirB].items[portalB]; + + float + averageA = (Point2.x(rangeA) + Point2.y(rangeA)) / 2f, + x1 = (moveDirs[dirA * 2] * averageA + (clusterA % cwidth) * clusterSize + offsets[dirA * 2] * (clusterSize - 1) + nextOffsets[dirA * 2] / 2f), + y1 = (moveDirs[dirA * 2 + 1] * averageA + (clusterA / cwidth) * clusterSize + offsets[dirA * 2 + 1] * (clusterSize - 1) + nextOffsets[dirA * 2 + 1] / 2f), + + averageB = (Point2.x(rangeB) + Point2.y(rangeB)) / 2f, + x2 = (moveDirs[dirB * 2] * averageB + (clusterB % cwidth) * clusterSize + offsets[dirB * 2] * (clusterSize - 1) + nextOffsets[dirB * 2] / 2f), + y2 = (moveDirs[dirB * 2 + 1] * averageB + (clusterB / cwidth) * clusterSize + offsets[dirB * 2 + 1] * (clusterSize - 1) + nextOffsets[dirB * 2 + 1] / 2f); + + return Math.abs(x1 - x2) + Math.abs(y1 - y2); + } + + @Nullable IntSeq clusterAstar(PathRequest request, int pathCost, int startNodeIndex, int endNodeIndex){ + var result = request.resultPath; + + if(startNodeIndex == endNodeIndex){ + result.clear(); + result.add(startNodeIndex); + return result; + } + + var team = request.team; + + if(request.costs == null) request.costs = new IntFloatMap(); + if(request.cameFrom == null) request.cameFrom = new IntIntMap(); + if(request.frontier == null) request.frontier = new PathfindQueue(); + + //note: these are NOT cleared, it is assumed that this function cleans up after itself at the end + //is this a good idea? don't know, might hammer the GC with unnecessary objects too + var costs = request.costs; + var cameFrom = request.cameFrom; + var frontier = request.frontier; + + cameFrom.put(startNodeIndex, startNodeIndex); + costs.put(startNodeIndex, 0); + frontier.add(startNodeIndex, 0); + + boolean foundEnd = false; + + while(frontier.size > 0){ + int current = frontier.poll(); + + if(current == endNodeIndex){ + foundEnd = true; + break; + } + + int cluster = NodeIndex.cluster(current), dir = NodeIndex.dir(current), portal = NodeIndex.portal(current); + int cx = cluster % cwidth, cy = cluster / cwidth; + Cluster clust = getCreateCluster(team, pathCost, cluster); + LongSeq innerCons = clust.portalConnections[dir] == null || portal >= clust.portalConnections[dir].length ? null : clust.portalConnections[dir][portal]; + + //edges for the cluster the node is 'in' + if(innerCons != null){ + checkEdges(request, team, pathCost, current, endNodeIndex, cx, cy, innerCons); + } + + //edges that this node 'faces' from the other side + int nextCx = cx + Geometry.d4[dir].x, nextCy = cy + Geometry.d4[dir].y; + if(nextCx >= 0 && nextCy >= 0 && nextCx < cwidth && nextCy < cheight){ + Cluster nextCluster = getCreateCluster(team, pathCost, nextCx, nextCy); + int relativeDir = (dir + 2) % 4; + LongSeq outerCons = nextCluster.portalConnections[relativeDir] == null ? null : nextCluster.portalConnections[relativeDir][portal]; + if(outerCons != null){ + checkEdges(request, team, pathCost, current, endNodeIndex, nextCx, nextCy, outerCons); + } + } + } + + //null them out, so they get GC'ed later + //there's no reason to keep them around and waste memory, since this path may never be recalculated + request.costs = null; + request.cameFrom = null; + request.frontier = null; + + if(foundEnd){ + result.clear(); + + int cur = endNodeIndex; + while(cur != startNodeIndex){ + result.add(cur); + cur = cameFrom.get(cur); + } + + result.reverse(); + + return result; + } + return null; + } + + private void checkEdges(PathRequest request, int team, int pathCost, int current, int goal, int cx, int cy, LongSeq connections){ + for(int i = 0; i < connections.size; i++){ + long con = connections.items[i]; + float cost = IntraEdge.cost(con); + int otherDir = IntraEdge.dir(con), otherPortal = IntraEdge.portal(con); + int next = makeNodeIndex(cx, cy, otherDir, otherPortal); + + float newCost = request.costs.get(current) + cost; + + if(newCost < request.costs.get(next, Float.POSITIVE_INFINITY)){ + request.costs.put(next, newCost); + + request.frontier.add(next, newCost + clusterNodeHeuristic(team, pathCost, next, goal)); + request.cameFrom.put(next, current); + } + } + } + + private void updateFields(FieldCache cache, long nsToRun){ + var frontier = cache.frontier; + var fields = cache.fields; + var goalPos = cache.goalPos; + var pcost = cache.cost; + var team = cache.team; + + long start = Time.nanos(); + int counter = 0; + + //actually do the flow field part + while(frontier.size > 0){ + int tile = frontier.removeLast(); + int baseX = tile % wwidth, baseY = tile / wwidth; + int curWeightIndex = (baseX / clusterSize) + (baseY / clusterSize) * cwidth; + + //TODO: how can this be null??? serious problem! + int[] curWeights = fields.get(curWeightIndex); + if(curWeights == null) continue; + + int cost = curWeights[baseX % clusterSize + ((baseY % clusterSize) * clusterSize)]; + + if(cost != impassable){ + for(Point2 point : Geometry.d4){ + + int + dx = baseX + point.x, dy = baseY + point.y, + clx = dx / clusterSize, cly = dy / clusterSize; + + if(clx < 0 || cly < 0 || dx >= wwidth || dy >= wheight) continue; + + int nextWeightIndex = clx + cly * cwidth; + + int[] weights = nextWeightIndex == curWeightIndex ? curWeights : fields.get(nextWeightIndex); + + //out of bounds; not allowed to move this way because no weights were registered here + if(weights == null) continue; + + int newPos = tile + point.x + point.y * wwidth; + + //can't move back to the goal + if(newPos == goalPos) continue; + + if(dx - clx * clusterSize < 0 || dy - cly * clusterSize < 0) continue; + + int newPosArray = (dx - clx * clusterSize) + (dy - cly * clusterSize) * clusterSize; + + int otherCost = pcost.getCost(team, pathfinder.tiles[newPos]); + int oldCost = weights[newPosArray]; + + //a cost of 0 means uninitialized, OR it means we're at the goal position, but that's handled above + if((oldCost == 0 || oldCost > cost + otherCost) && otherCost != impassable){ + frontier.addFirst(newPos); + weights[newPosArray] = cost + otherCost; + } + } + } + + //every N iterations, check the time spent - this prevents extra calls to nano time, which itself is slow + if(nsToRun >= 0 && (counter++) >= updateStepInterval){ + counter = 0; + if(Time.timeSinceNanos(start) >= nsToRun){ + return; + } + } + } + } + + private void addFlowCluster(FieldCache cache, int cluster, boolean addingFrontier){ + addFlowCluster(cache, cluster % cwidth, cluster / cwidth, addingFrontier); + } + + private void addFlowCluster(FieldCache cache, int cx, int cy, boolean addingFrontier){ + //out of bounds + if(cx < 0 || cy < 0 || cx >= cwidth || cy >= cheight) return; + + var fields = cache.fields; + int key = cx + cy * cwidth; + + if(!fields.containsKey(key)){ + fields.put(key, new int[clusterSize * clusterSize]); + + if(addingFrontier){ + for(int dir = 0; dir < 4; dir++){ + int ox = cx + nextOffsets[dir * 2], oy = cy + nextOffsets[dir * 2 + 1]; + + if(ox < 0 || oy < 0 || ox >= cwidth || ox >= cheight) continue; + + var otherField = cache.fields.get(ox + oy * cwidth); + + if(otherField == null) continue; + + int + relOffset = (dir + 2) % 4, + movex = moveDirs[relOffset * 2], + movey = moveDirs[relOffset * 2 + 1], + otherx1 = offsets[relOffset * 2] * (clusterSize - 1), + othery1 = offsets[relOffset * 2 + 1] * (clusterSize - 1); + + //scan the edge of the cluster + for(int i = 0; i < clusterSize; i++){ + int x = otherx1 + movex * i, y = othery1 + movey * i; + + //check to make sure it's not 0 (uninitialized flowfield data) + if(otherField[x + y * clusterSize] > 0){ + int worldX = x + ox * clusterSize, worldY = y + oy * clusterSize; + + //add the world-relative position to the frontier, so it recalculates + cache.frontier.addFirst(worldX + worldY * wwidth); + + if(showDebug){ + Core.app.post(() -> Fx.placeBlock.at(worldX *tilesize, worldY * tilesize, 1f)); + } + } + } + } + } + } + } + + private void initializePathRequest(PathRequest request, int team, int costId, int unitX, int unitY, int goalX, int goalY){ + PathCost pcost = idToCost(costId); + + int goalPos = (goalX + goalY * wwidth); + + int node = findClosestNode(team, costId, unitX, unitY); + int dest = findClosestNode(team, costId, goalX, goalY); + + if(dest == Integer.MAX_VALUE){ + request.notFound = true; + //no node found (TODO: invalid state??) + return; + } + + var nodePath = clusterAstar(request, costId, node, dest); + + FieldCache cache = fields.get(Pack.longInt(goalPos, costId)); + //if true, extra values are added on the sides of existing field cells that face new cells. + boolean addingFrontier = true; + + //create the cache if it doesn't exist, and initialize it + if(cache == null){ + cache = new FieldCache(pcost, costId, team, goalPos); + fields.put(cache.mapKey, cache); + FieldCache fcache = cache; + //register field in main thread for iteration + Core.app.post(() -> fieldList.add(fcache)); + cache.frontier.addFirst(goalPos); + addingFrontier = false; //when it's a new field, there is no need to add to the frontier to merge the flowfield + } + + if(nodePath != null){ + int cx = unitX / clusterSize, cy = unitY / clusterSize; + + addFlowCluster(cache, cx, cy, addingFrontier); + + for(int i = -1; i < nodePath.size; i++){ + int + current = i == -1 ? node : nodePath.items[i], + cluster = NodeIndex.cluster(current), + dir = NodeIndex.dir(current), + dx = Geometry.d4[dir].x, + dy = Geometry.d4[dir].y, + ox = cluster % cwidth + dx, + oy = cluster / cwidth + dy; + + addFlowCluster(cache, cluster, addingFrontier); + + //store directional/flipped version of cluster + if(ox >= 0 && oy >= 0 && ox < cwidth && oy < cheight){ + int other = ox + oy * cwidth; + + addFlowCluster(cache, other, addingFrontier); + } + } + } + } + + private PathCost idToCost(int costId){ + return ControlPathfinder.costTypes.get(costId); } public static boolean isNearObstacle(Unit unit, int x1, int y1, int x2, int y2){ return raycast(unit.team().id, unit.type.pathCost, x1, y1, x2, y2); } + @Deprecated + public int nextTargetId(){ + return 0; + } + + @Deprecated + public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 out){ + return getPathPosition(unit, pathId, destination, out, null); + } + + @Deprecated + public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 out, @Nullable boolean[] noResultFound){ + return getPathPosition(unit, destination, destination, out, noResultFound); + } + + public boolean getPathPosition(Unit unit, Vec2 destination, Vec2 mainDestination, Vec2 out, @Nullable boolean[] noResultFound){ + int costId = unit.type.pathCostId; + PathCost cost = idToCost(costId); + + int + team = unit.team.id, + tileX = unit.tileX(), + tileY = unit.tileY(), + packedPos = world.packArray(tileX, tileY), + destX = World.toTile(mainDestination.x), + destY = World.toTile(mainDestination.y), + actualDestX = World.toTile(destination.x), + actualDestY = World.toTile(destination.y), + destPos = destX + destY * wwidth; + + PathRequest request = unitRequests.get(unit); + + unit.hitboxTile(Tmp.r3); + //tile rect size has tile size factored in, since the ray cannot have thickness + float tileRectSize = tilesize + Tmp.r3.height; + + int lastRaycastTile = request == null || world.tileChanges != request.lastWorldUpdate ? -1 : request.lastRaycastTile; + boolean raycastResult = request != null && request.lastRaycastResult; + + //cache raycast results to run every time the world updates, and every tile the unit crosses + if(lastRaycastTile != packedPos){ + //near the destination, standard raycasting tends to break down, so use the more permissive 'near' variant that doesn't take into account edges of walls + raycastResult = unit.within(destination, tilesize * 2.5f) ? !raycastRect(unit.x, unit.y, destination.x, destination.y, team, cost, tileX, tileY, actualDestX, actualDestY, tileRectSize) : !raycast(team, cost, tileX, tileY, actualDestX, actualDestY); + + if(request != null){ + request.lastRaycastTile = packedPos; + request.lastRaycastResult = raycastResult; + request.lastWorldUpdate = world.tileChanges; + } + } + + //if the destination can be trivially reached in a straight line, do that. + if(raycastResult){ + out.set(destination); + return true; + } + + boolean any = false; + + long fieldKey = Pack.longInt(destPos, costId); + + //use existing request if it exists. + if(request != null && request.destination == destPos){ + request.lastUpdateId = state.updateId; + + Tile tileOn = unit.tileOn(), initialTileOn = tileOn; + //TODO: should fields be accessible from this thread? + FieldCache fieldCache = fields.get(fieldKey); + + if(fieldCache != null && tileOn != null){ + FieldCache old = request.oldCache; + //nullify the old field to be GCed, as it cannot be relevant anymore (this path is complete) + if(fieldCache.frontier.isEmpty() && old != null){ + request.oldCache = null; + } + + fieldCache.lastUpdateId = state.updateId; + int maxIterations = 30; //TODO higher/lower number? is this still too slow? + int i = 0; + boolean recalc = false; + + //TODO last pos can change if the flowfield changes. + if(initialTileOn.pos() != request.lastTile || request.lastTargetTile == null){ + boolean anyNearSolid = false; + + //find the next tile until one near a solid block is discovered + while(i ++ < maxIterations){ + int value = getCost(fieldCache, old, tileOn.x, tileOn.y); + + Tile current = null; + int minCost = 0; + for(int dir = 0; dir < 4; dir ++){ + Point2 point = Geometry.d4[dir]; + int dx = tileOn.x + point.x, dy = tileOn.y + point.y; + + Tile other = world.tile(dx, dy); + + if(other == null) continue; + + int packed = world.packArray(dx, dy); + int otherCost = getCost(fieldCache, old, dx, dy), relCost = otherCost - value; + + if(relCost > 2 || otherCost <= 0){ + anyNearSolid = true; + } + + if((value == 0 || otherCost < value) && otherCost != impassable && (otherCost != 0 || packed == destPos) && (current == null || otherCost < minCost) && passable(unit.team.id, cost, packed)){ + current = other; + minCost = otherCost; + } + } + + //TODO raycast spam = extremely slow + //...flowfield integration spam is also really slow. + if(!(current == null || (costId == costIdGround && current.dangerous() && !tileOn.dangerous()))){ + + //when anyNearSolid is false, no solid tiles have been encountered anywhere so far, so raycasting is a waste of time + if(anyNearSolid && !tileOn.dangerous() && raycastRect(unit.x, unit.y, current.x * tilesize, current.y * tilesize, team, cost, initialTileOn.x, initialTileOn.y, current.x, current.y, tileRectSize)){ + + //TODO this may be a mistake + if(tileOn == initialTileOn){ + recalc = true; + any = true; + } + + break; + }else{ + tileOn = current; + any = true; + + if(current.array() == destPos){ + break; + } + } + + }else{ + break; + } + } + + request.lastTargetTile = any ? tileOn : null; + if(showDebug && tileOn != null){ + Fx.placeBlock.at(tileOn.worldx(), tileOn.worldy(), 1); + } + } + + if(request.lastTargetTile != null){ + out.set(request.lastTargetTile); + request.lastTile = recalc ? -1 : initialTileOn.pos(); + return true; + } + } + }else if(request == null){ + + //queue new request. + unitRequests.put(unit, request = new PathRequest(unit, team, costId, destPos)); + + PathRequest f = request; + + //on the pathfinding thread: initialize the request + queue.post(() -> { + threadPathRequests.add(f); + recalculatePath(f); + }); + + out.set(destination); + + return true; + } + + if(noResultFound != null){ + noResultFound[0] = request.notFound; + } + return false; + } + + private void recalculatePath(PathRequest request){ + initializePathRequest(request, request.team, request.costId, request.unit.tileX(), request.unit.tileY(), request.destination % wwidth, request.destination / wwidth); + } + + private int getCost(FieldCache cache, FieldCache old, int x, int y){ + //fall back to the old flowfield when possible - it's best not to use partial results from the base cache + if(old != null){ + return getCost(old, x, y, false); + } + return getCost(cache, x, y, true); + } + + private int getCost(FieldCache cache, int x, int y, boolean requeue){ + int[] field = cache.fields.get(x / clusterSize + (y / clusterSize) * cwidth); + if(field == null){ + if(!requeue) return 0; + //request a new flow cluster if one wasn't found; this may be a spammed a bit, but the function will return early once it's created the first time + queue.post(() -> addFlowCluster(cache, x / clusterSize, y / clusterSize, true)); + return 0; + } + return field[(x % clusterSize) + (y % clusterSize) * clusterSize]; + } + private static boolean raycast(int team, PathCost type, int x1, int y1, int x2, int y2){ int ww = wwidth, wh = wheight; int x = x1, dx = Math.abs(x2 - x), sx = x < x2 ? 1 : -1; @@ -374,17 +1268,6 @@ public class ControlPathfinder{ if(avoid(team, type, x + y * wwidth)) return true; if(x == x2 && y == y2) return false; - //TODO no diagonals???? is this a good idea? - /* - //no diagonal ver - if(2 * err + dy > dx - 2 * err){ - err -= dy; - x += sx; - }else{ - err += dx; - y += sy; - }*/ - //diagonal ver e2 = 2 * err; if(e2 > -dy){ @@ -396,30 +1279,6 @@ public class ControlPathfinder{ err += dx; y += sy; } - - } - - return true; - } - - private static boolean permissiveRaycast(int team, PathCost type, int x1, int y1, int x2, int y2){ - int ww = wwidth, wh = wheight; - int x = x1, dx = Math.abs(x2 - x), sx = x < x2 ? 1 : -1; - int y = y1, dy = Math.abs(y2 - y), sy = y < y2 ? 1 : -1; - int err = dx - dy; - - while(x >= 0 && y >= 0 && x < ww && y < wh){ - if(solid(team, type, x + y * wwidth, true)) return true; - if(x == x2 && y == y2) return false; - - //no diagonals - if(2 * err + dy > dx - 2 * err){ - err -= dy; - x += sx; - }else{ - err += dx; - y += sy; - } } return true; @@ -449,22 +1308,65 @@ public class ControlPathfinder{ return 0; } - static boolean cast(int team, PathCost cost, int from, int to){ - return raycast(team, cost, from % wwidth, from / wwidth, to % wwidth, to / wwidth); + private static boolean overlap(int team, PathCost type, int x, int y, float startX, float startY, float endX, float endY, float rectSize){ + if(x < 0 || y < 0 || x >= wwidth || y >= wheight) return false; + if(!passable(team, type, x + y * wwidth)){ + return Intersector.intersectSegmentRectangleFast(startX, startY, endX, endY, x * tilesize - rectSize/2f, y * tilesize - rectSize/2f, rectSize, rectSize); + } + return false; } - private Tile tile(int pos){ - return world.tiles.geti(pos); + private static boolean raycastRect(float startX, float startY, float endX, float endY, int team, PathCost type, int x1, int y1, int x2, int y2, float rectSize){ + int ww = wwidth, wh = wheight; + int x = x1, dx = Math.abs(x2 - x), sx = x < x2 ? 1 : -1; + int y = y1, dy = Math.abs(y2 - y), sy = y < y2 ? 1 : -1; + int e2, err = dx - dy; + + while(x >= 0 && y >= 0 && x < ww && y < wh){ + if( + !passable(team, type, x + y * wwidth) || + overlap(team, type, x + 1, y, startX, startY, endX, endY, rectSize) || + overlap(team, type, x - 1, y, startX, startY, endX, endY, rectSize) || + overlap(team, type, x, y + 1, startX, startY, endX, endY, rectSize) || + overlap(team, type, x, y - 1, startX, startY, endX, endY, rectSize) + ) return true; + + if(x == x2 && y == y2) return false; + + //diagonal ver + e2 = 2 * err; + if(e2 > -dy){ + err -= dy; + x += sx; + } + + if(e2 < dx){ + err += dx; + y += sy; + } + } + + return true; } - //distance heuristic: manhattan - private static float heuristic(int a, int b){ - int x = a % wwidth, x2 = b % wwidth, y = a / wwidth, y2 = b / wwidth; - return Math.abs(x - x2) + Math.abs(y - y2); + private static boolean avoid(int team, PathCost type, int tilePos){ + int cost = cost(team, type, tilePos); + return cost == impassable || cost >= 2; } - private static int tcost(int team, PathCost cost, int tilePos){ - return cost.getCost(team, pathfinder.tiles[tilePos]); + private static boolean passable(int team, PathCost cost, int pos){ + int amount = cost.getCost(team, pathfinder.tiles[pos]); + //edge case: naval reports costs of 6000+ for non-liquids, even though they are not technically passable + return amount != impassable && !(cost == costNaval && amount >= 6000); + } + + private static boolean solid(int team, PathCost type, int x, int y){ + return x < 0 || y < 0 || x >= wwidth || y >= wheight || solid(team, type, x + y * wwidth, true); + } + + private static boolean solid(int team, PathCost type, int tilePos, boolean checkWall){ + int cost = cost(team, type, tilePos); + return cost == impassable || (checkWall && cost >= 6000); } private static int cost(int team, PathCost cost, int tilePos){ @@ -477,246 +1379,162 @@ public class ControlPathfinder{ return cost.getCost(team, pathfinder.tiles[tilePos]); } - private static boolean avoid(int team, PathCost type, int tilePos){ - int cost = cost(team, type, tilePos); - return cost == impassable || cost >= 2; - } + private void clusterChanged(int team, int pathCost, int cx, int cy){ + int index = cx + cy * cwidth; - private static boolean solid(int team, PathCost type, int tilePos, boolean checkWall){ - int cost = cost(team, type, tilePos); - return cost == impassable || (checkWall && cost >= 6000); - } - - private static float tileCost(int team, PathCost type, int a, int b){ - //currently flat cost - return cost(team, type, b); - } - - static class PathfindThread extends Thread{ - /** handles task scheduling on the update thread. */ - TaskQueue queue = new TaskQueue(); - /** pathfinding thread access only! */ - Seq requests = new Seq<>(); - /** volatile for access across threads */ - volatile int requestSize; - - public PathfindThread(String name){ - super(name); + for(var req : threadPathRequests){ + long mapKey = Pack.longInt(req.destination, pathCost); + var field = fields.get(mapKey); + if((field != null && field.fields.containsKey(index)) || req.notFound){ + invalidRequests.add(req); + } } - @Override - public void run(){ - while(true){ - //stop on client, no updating - if(net.client()) return; - try{ - if(state.isPlaying()){ - queue.run(); - requestSize = requests.size; + } - //total update time no longer than maxUpdate - for(var req : requests){ - //TODO this is flawed with many paths - req.update(maxUpdate / requests.size); + private void updateClustersComplete(int clusterIndex){ + for(int team = 0; team < clusters.length; team++){ + var dim1 = clusters[team]; + if(dim1 != null){ + for(int pathCost = 0; pathCost < dim1.length; pathCost++){ + var dim2 = dim1[pathCost]; + if(dim2 != null){ + var cluster = dim2[clusterIndex]; + if(cluster != null){ + updateCluster(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth); + clusterChanged(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth); + } + } + } + } + } + } + + private void updateClustersInner(int clusterIndex){ + for(int team = 0; team < clusters.length; team++){ + var dim1 = clusters[team]; + if(dim1 != null){ + for(int pathCost = 0; pathCost < dim1.length; pathCost++){ + var dim2 = dim1[pathCost]; + if(dim2 != null){ + var cluster = dim2[clusterIndex]; + if(cluster != null){ + updateInnerEdges(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth, cluster); + clusterChanged(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth); + } + } + } + } + } + } + + @Override + public void run(){ + long lastInvalidCheck = Time.millis() + invalidateCheckInterval; + + while(true){ + if(net.client()) return; + try{ + + + if(state.isPlaying()){ + queue.run(); + + clustersToUpdate.each(cluster -> { + updateClustersComplete(cluster); + + //just in case: don't redundantly update inner clusters after you've recalculated it entirely + clustersToInnerUpdate.remove(cluster); + }); + + clustersToInnerUpdate.each(cluster -> { + //only recompute the inner links + updateClustersInner(cluster); + }); + + clustersToInnerUpdate.clear(); + clustersToUpdate.clear(); + + //periodically check for invalidated paths + if(Time.timeSinceMillis(lastInvalidCheck) > invalidateCheckInterval){ + lastInvalidCheck = Time.millis(); + + var it = invalidRequests.iterator(); + while(it.hasNext()){ + var request = it.next(); + + //invalid request, ignore it + if(request.invalidated){ + it.remove(); + continue; + } + + long mapKey = Pack.longInt(request.destination, request.costId); + + var field = fields.get(mapKey); + + if(field != null){ + //it's only worth recalculating a path when the current frontier has finished; otherwise the unit will be following something incomplete. + if(field.frontier.isEmpty()){ + + //remove the field, to be recalculated next update one recalculatePath is processed + fields.remove(field.mapKey); + Core.app.post(() -> fieldList.remove(field)); + + //once the field is invalidated, make sure that all the requests that have it stored in their 'old' field, so units don't stutter during recalculations + for(var otherRequest : threadPathRequests){ + if(otherRequest.destination == request.destination){ + otherRequest.oldCache = field; + } + } + + //the recalculation is done next update, so multiple path requests in the same batch don't end up removing and recalculating the field multiple times. + queue.post(() -> recalculatePath(request)); + //it has been processed. + it.remove(); + } + }else{ //there's no field, presumably because a previous request already invalidated it. + queue.post(() -> recalculatePath(request)); + it.remove(); + } } } - try{ - Thread.sleep(updateInterval); - }catch(InterruptedException e){ - //stop looping when interrupted externally - return; + //each update time (not total!) no longer than maxUpdate + for(FieldCache cache : fields.values()){ + updateFields(cache, maxUpdate); } - }catch(Throwable e){ - //do not crash the pathfinding thread - Log.err(e); } + + try{ + Thread.sleep(updateInterval); + }catch(InterruptedException e){ + //stop looping when interrupted externally + return; + } + }catch(Throwable e){ + e.printStackTrace(); } } } - static class PathRequest{ - final PathfindThread thread; + @Struct + static class IntraEdgeStruct{ + @StructField(8) + int dir; + @StructField(8) + int portal; - volatile boolean done = false; - volatile boolean foundEnd = false; - volatile Unit unit; - volatile PathCost cost; - volatile int team; - volatile int lastWorldUpdate; - volatile boolean forcedRecalc; + float cost; + } - final Vec2 lastPos = new Vec2(); - float stuckTimer = 0f; - - final Vec2 destination = new Vec2(); - final Vec2 lastDestination = new Vec2(); - - //TODO only access on main thread?? - volatile int pathIndex; - - int rayPathIndex = -1; - IntSeq result = new IntSeq(); - volatile float raycastTimer; - - PathfindQueue frontier = new PathfindQueue(); - //node index -> node it came from - IntIntMap cameFrom = new IntIntMap(); - //node index -> total cost - IntFloatMap costs = new IntFloatMap(); - - int start, goal; - - long lastUpdateId; - long lastTime; - long forceRecalcTime; - - volatile int lastId, curId; - - public PathRequest(PathfindThread thread){ - this.thread = thread; - } - - public void forceRecalculate(){ - //keep it at 3 times/sec - if(Time.timeSinceMillis(forceRecalcTime) < 1000 / 3) return; - forcedRecalc = true; - forceRecalcTime = Time.millis(); - } - - void update(long maxUpdateNs){ - if(curId != lastId){ - clear(true); - } - lastId = curId; - - //re-do everything when world updates, but keep the old path around - if(forcedRecalc || (Time.timeSinceMillis(lastTime) > 1000 * 3 && (worldUpdateId != lastWorldUpdate || !destination.epsilonEquals(lastDestination, 2f)))){ - lastTime = Time.millis(); - lastWorldUpdate = worldUpdateId; - forcedRecalc = false; - clear(false); - } - - if(done) return; - - long ns = Time.nanos(); - int counter = 0; - - while(frontier.size > 0){ - int current = frontier.poll(); - - if(current == goal){ - foundEnd = true; - break; - } - - int cx = current % wwidth, cy = current / wwidth; - - for(Point2 point : Geometry.d4){ - int newx = cx + point.x, newy = cy + point.y; - int next = newx + wwidth * newy; - - if(newx >= wwidth || newy >= wheight || newx < 0 || newy < 0) continue; - - //in fallback mode, enemy walls are passable - if(tcost(team, cost, next) == impassable) continue; - - float add = tileCost(team, cost, current, next); - float currentCost = costs.get(current); - - if(add < 0) continue; - - //the cost can include an impassable enemy wall, so cap the cost if so and add the base cost instead - //essentially this means that any path with enemy walls will only count the walls once, preventing strange behavior like avoiding based on wall count - float newCost = currentCost >= wallImpassableCap && add >= wallImpassableCap ? currentCost + add - wallImpassableCap : currentCost + add; - - //a cost of 0 means "not set" - if(!costs.containsKey(next) || newCost < costs.get(next)){ - costs.put(next, newCost); - float priority = newCost + heuristic(next, goal); - frontier.add(next, priority); - cameFrom.put(next, current); - } - } - - //only check every N iterations to prevent nanoTime spam (slow) - if((counter ++) >= 100){ - counter = 0; - - //exit when out of time. - if(Time.timeSinceNanos(ns) > maxUpdateNs){ - return; - } - } - } - - lastTime = Time.millis(); - raycastTimer = 9999f; - result.clear(); - - pathIndex = 0; - rayPathIndex = -1; - - if(foundEnd){ - int cur = goal; - while(cur != start){ - result.add(cur); - cur = cameFrom.get(cur); - } - - result.reverse(); - - smoothPath(); - } - - //don't keep this around in memory, better to dump entirely - using clear() keeps around massive arrays for paths - frontier = new PathfindQueue(); - cameFrom = new IntIntMap(); - costs = new IntFloatMap(); - - done = true; - } - - void smoothPath(){ - int len = result.size; - if(len <= 2) return; - - int output = 1, input = 2; - - while(input < len){ - if(cast(team, cost, result.get(output - 1), result.get(input))){ - result.swap(output, input - 1); - output++; - } - input++; - } - - result.swap(output, input - 1); - result.size = output + 1; - } - - void clear(boolean resetCurrent){ - done = false; - - frontier = new PathfindQueue(20); - cameFrom.clear(); - costs.clear(); - - start = world.packArray(unit.tileX(), unit.tileY()); - goal = world.packArray(World.toTile(destination.x), World.toTile(destination.y)); - - cameFrom.put(start, start); - costs.put(start, 0); - - frontier.add(start, 0); - - foundEnd = false; - lastDestination.set(destination); - - if(resetCurrent){ - result.clear(); - } - } + @Struct + static class NodeIndexStruct{ + @StructField(22) + int cluster; + @StructField(2) + int dir; + @StructField(8) + int portal; } } diff --git a/core/src/mindustry/ai/HierarchyPathFinder.java b/core/src/mindustry/ai/HierarchyPathFinder.java deleted file mode 100644 index 7d43a2bdcd..0000000000 --- a/core/src/mindustry/ai/HierarchyPathFinder.java +++ /dev/null @@ -1,1471 +0,0 @@ -package mindustry.ai; - -import arc.*; -import arc.graphics.*; -import arc.graphics.g2d.*; -import arc.math.*; -import arc.math.geom.*; -import arc.struct.*; -import arc.util.*; -import mindustry.annotations.Annotations.*; -import mindustry.content.*; -import mindustry.core.*; -import mindustry.game.EventType.*; -import mindustry.game.*; -import mindustry.gen.*; -import mindustry.graphics.*; -import mindustry.world.*; - -import static mindustry.Vars.*; -import static mindustry.ai.Pathfinder.*; - -//https://webdocs.cs.ualberta.ca/~mmueller/ps/hpastar.pdf -//https://www.gameaipro.com/GameAIPro/GameAIPro_Chapter23_Crowd_Pathfinding_and_Steering_Using_Flow_Field_Tiles.pdf -public class HierarchyPathFinder implements Runnable{ - private static final long maxUpdate = Time.millisToNanos(12); - private static final int updateStepInterval = 200; - private static final int updateFPS = 30; - private static final int updateInterval = 1000 / updateFPS, invalidateCheckInterval = 1000; - - static final int clusterSize = 12; - - static final boolean debug = OS.hasProp("mindustry.debug"); - - static final int[] offsets = { - 1, 0, //right: bottom to top - 0, 1, //top: left to right - 0, 0, //left: bottom to top - 0, 0 //bottom: left to right - }; - - static final int[] moveDirs = { - 0, 1, - 1, 0, - 0, 1, - 1, 0 - }; - - static final int[] nextOffsets = { - 1, 0, - 0, 1, - -1, 0, - 0, -1 - }; - - //maps team -> pathCost -> flattened array of clusters in 2D - //(what about teams? different path costs?) - Cluster[][][] clusters; - - int cwidth, cheight; - - //temporarily used for resolving connections for intra-edges - IntSet usedEdges = new IntSet(); - //tasks to run on pathfinding thread - TaskQueue queue = new TaskQueue(); - - //individual requests based on unit - MAIN THREAD ONLY - ObjectMap unitRequests = new ObjectMap<>(); - - Seq threadPathRequests = new Seq<>(false); - - //TODO: very dangerous usage; - //TODO - it is accessed from the main thread - //TODO - it is written to on the pathfinding thread - //TODO - it does not include - //maps position in world in (x + y * width format) | type (bitpacked to long) to a cache of flow fields - LongMap fields = new LongMap<>(); - //MAIN THREAD ONLY - Seq fieldList = new Seq<>(false); - - //these are for inner edge A* (temporary!) - IntFloatMap innerCosts = new IntFloatMap(); - PathfindQueue innerFrontier = new PathfindQueue(); - - //ONLY modify on pathfinding thread. - IntSet clustersToUpdate = new IntSet(); - IntSet clustersToInnerUpdate = new IntSet(); - - //PATHFINDING THREAD - requests that should be recomputed - ObjectSet invalidRequests = new ObjectSet<>(); - - /** Current pathfinding thread */ - @Nullable Thread thread; - - //path requests are per-unit - static class PathRequest{ - final Unit unit; - final int destination, team, costId; - //resulting path of nodes - final IntSeq resultPath = new IntSeq(); - - //node index -> total cost - @Nullable IntFloatMap costs = new IntFloatMap(); - //node index (NodeIndex struct) -> node it came from TODO merge them, make properties of FieldCache? - @Nullable IntIntMap cameFrom = new IntIntMap(); - //frontier for A* - @Nullable PathfindQueue frontier = new PathfindQueue(); - - //main thread only! - long lastUpdateId = state.updateId; - - //both threads - volatile boolean notFound = false; - volatile boolean invalidated = false; - //old field assigned before everything was recomputed - @Nullable volatile FieldCache oldCache; - - boolean lastRaycastResult = false; - int lastRaycastTile, lastWorldUpdate; - int lastTile; - @Nullable Tile lastTargetTile; - - PathRequest(Unit unit, int team, int costId, int destination){ - this.unit = unit; - this.costId = costId; - this.team = team; - this.destination = destination; - } - } - - static class FieldCache{ - final PathCost cost; - final int costId; - final int team; - final int goalPos; - //frontier for flow fields - final IntQueue frontier = new IntQueue(); - //maps cluster index to field weights; 0 means uninitialized - final IntMap fields = new IntMap<>(); - final long mapKey; - - //main thread only! - long lastUpdateId = state.updateId; - - //TODO: how are the nodes merged? CAN they be merged? - - FieldCache(PathCost cost, int costId, int team, int goalPos){ - this.cost = cost; - this.team = team; - this.goalPos = goalPos; - this.costId = costId; - this.mapKey = Pack.longInt(goalPos, costId); - } - } - - static class Cluster{ - IntSeq[] portals = new IntSeq[4]; - //maps rotation + index of portal to list of IntraEdge objects - LongSeq[][] portalConnections = new LongSeq[4][]; - } - - public HierarchyPathFinder(){ - - Events.on(ResetEvent.class, event -> stop()); - - Events.on(WorldLoadEvent.class, event -> { - stop(); - - //TODO: can the pathfinding thread even see these? - unitRequests = new ObjectMap<>(); - fields = new LongMap<>(); - fieldList = new Seq<>(false); - - clusters = new Cluster[256][][]; - cwidth = Mathf.ceil((float)world.width() / clusterSize); - cheight = Mathf.ceil((float)world.height() / clusterSize); - - - start(); - }); - - Events.on(TileChangeEvent.class, e -> { - - e.tile.getLinkedTiles(t -> { - int x = t.x, y = t.y, mx = x % clusterSize, my = y % clusterSize, cx = x / clusterSize, cy = y / clusterSize, cluster = cx + cy * cwidth; - - //is at the edge of a cluster; this means the portals may have changed. - if(mx == 0 || my == 0 || mx == clusterSize - 1 || my == clusterSize - 1){ - - if(mx == 0) queueClusterUpdate(cx - 1, cy); //left - if(my == 0) queueClusterUpdate(cx, cy - 1); //bottom - if(mx == clusterSize - 1) queueClusterUpdate(cx + 1, cy); //right - if(my == clusterSize - 1) queueClusterUpdate(cx, cy + 1); //top - - queueClusterUpdate(cx, cy); - //TODO: recompute edge clusters too. - }else{ - //there is no need to recompute portals for block updates that are not on the edge. - queue.post(() -> clustersToInnerUpdate.add(cluster)); - } - }); - - //TODO: recalculate affected flow fields? or just all of them? how to reflow? - }); - - //invalidate paths - Events.run(Trigger.update, () -> { - for(var req : unitRequests.values()){ - //skipped N update -> drop it - if(req.lastUpdateId <= state.updateId - 10){ - req.invalidated = true; - //concurrent modification! - queue.post(() -> threadPathRequests.remove(req)); - Core.app.post(() -> unitRequests.remove(req.unit)); - } - } - - for(var field : fieldList){ - //skipped N update -> drop it - if(field.lastUpdateId <= state.updateId - 30){ - //make sure it's only modified on the main thread...? but what about calling get() on this thread?? - queue.post(() -> fields.remove(field.mapKey)); - Core.app.post(() -> fieldList.remove(field)); - } - } - }); - - if(debug){ - Events.run(Trigger.draw, () -> { - int team = player.team().id; - int cost = 0; - - Draw.draw(Layer.overlayUI, () -> { - Lines.stroke(1f); - - if(clusters[team] != null && clusters[team][cost] != null){ - for(int cx = 0; cx < cwidth; cx++){ - for(int cy = 0; cy < cheight; cy++){ - - var cluster = clusters[team][cost][cy * cwidth + cx]; - if(cluster != null){ - Lines.stroke(0.5f); - Draw.color(Color.gray); - Lines.stroke(1f); - - Lines.rect(cx * clusterSize * tilesize - tilesize/2f, cy * clusterSize * tilesize - tilesize/2f, clusterSize * tilesize, clusterSize * tilesize); - - - for(int d = 0; d < 4; d++){ - IntSeq portals = cluster.portals[d]; - if(portals != null){ - - for(int i = 0; i < portals.size; i++){ - int pos = portals.items[i]; - int from = Point2.x(pos), to = Point2.y(pos); - float width = tilesize * (Math.abs(from - to) + 1), height = tilesize; - - portalToVec(cluster, cx, cy, d, i, Tmp.v1); - - Draw.color(Color.brown); - Lines.ellipse(30, Tmp.v1.x, Tmp.v1.y, width / 2f, height / 2f, d * 90f - 90f); - - LongSeq connections = cluster.portalConnections[d] == null ? null : cluster.portalConnections[d][i]; - - if(connections != null){ - Draw.color(Color.forest); - for(int coni = 0; coni < connections.size; coni ++){ - long con = connections.items[coni]; - - portalToVec(cluster, cx, cy, IntraEdge.dir(con), IntraEdge.portal(con), Tmp.v2); - - float - x1 = Tmp.v1.x, y1 = Tmp.v1.y, - x2 = Tmp.v2.x, y2 = Tmp.v2.y; - Lines.line(x1, y1, x2, y2); - - } - } - } - } - } - } - } - } - } - - for(var fields : fieldList){ - try{ - for(var entry : fields.fields){ - int cx = entry.key % cwidth, cy = entry.key / cwidth; - for(int y = 0; y < clusterSize; y++){ - for(int x = 0; x < clusterSize; x++){ - int value = entry.value[x + y * clusterSize]; - Tmp.c1.a = 1f; - Lines.stroke(0.8f, Tmp.c1.fromHsv(value * 3f, 1f, 1f)); - Draw.alpha(0.5f); - Fill.square((x + cx * clusterSize) * tilesize, (y + cy * clusterSize) * tilesize, tilesize / 2f); - } - } - } - }catch(Exception ignored){} //probably has some concurrency issues when iterating but I don't care, this is for debugging - } - }); - - Draw.reset(); - }); - } - } - - void queueClusterUpdate(int cx, int cy){ - if(cx >= 0 && cy >= 0 && cx < cwidth && cy < cheight){ - queue.post(() -> clustersToUpdate.add(cx + cy * cwidth)); - } - } - - //debugging only! - void portalToVec(Cluster cluster, int cx, int cy, int direction, int portalIndex, Vec2 out){ - int pos = cluster.portals[direction].items[portalIndex]; - int from = Point2.x(pos), to = Point2.y(pos); - int addX = moveDirs[direction * 2], addY = moveDirs[direction * 2 + 1]; - float average = (from + to) / 2f; - - float - x = (addX * average + cx * clusterSize + offsets[direction * 2] * (clusterSize - 1) + nextOffsets[direction * 2] / 2f) * tilesize, - y = (addY * average + cy * clusterSize + offsets[direction * 2 + 1] * (clusterSize - 1) + nextOffsets[direction * 2 + 1] / 2f) * tilesize; - - out.set(x, y); - } - - /** Starts or restarts the pathfinding thread. */ - private void start(){ - stop(); - if(net.client()) return; - - thread = new Thread(this, "Control Pathfinder"); - thread.setPriority(Thread.MIN_PRIORITY); - thread.setDaemon(true); - thread.start(); - } - - /** Stops the pathfinding thread. */ - private void stop(){ - if(thread != null){ - thread.interrupt(); - thread = null; - } - queue.clear(); - } - - /** @return a cluster at coordinates; can be null if not cluster was created yet*/ - @Nullable Cluster getCluster(int team, int pathCost, int cx, int cy){ - return getCluster(team, pathCost, cx + cy * cwidth); - } - - /** @return a cluster at coordinates; can be null if not cluster was created yet*/ - @Nullable Cluster getCluster(int team, int pathCost, int clusterIndex){ - if(clusters == null) return null; - - Cluster[][] dim1 = clusters[team]; - - if(dim1 == null) return null; - - Cluster[] dim2 = dim1[pathCost]; - - if(dim2 == null) return null; - - return dim2[clusterIndex]; - } - - /** @return the cluster at specified coordinates; never null. */ - Cluster getCreateCluster(int team, int pathCost, int cx, int cy){ - return getCreateCluster(team, pathCost, cx + cy * cwidth); - } - - /** @return the cluster at specified coordinates; never null. */ - Cluster getCreateCluster(int team, int pathCost, int clusterIndex){ - Cluster result = getCluster(team, pathCost, clusterIndex); - if(result == null){ - return updateCluster(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth); - }else{ - return result; - } - } - - Cluster updateCluster(int team, int pathCost, int cx, int cy){ - //TODO: what if clusters are null for thread visibility reasons? - - Cluster[][] dim1 = clusters[team]; - - if(dim1 == null){ - dim1 = clusters[team] = new Cluster[Team.all.length][]; - } - - Cluster[] dim2 = dim1[pathCost]; - - if(dim2 == null){ - dim2 = dim1[pathCost] = new Cluster[cwidth * cheight]; - } - - Cluster cluster = dim2[cy * cwidth + cx]; - if(cluster == null){ - cluster = dim2[cy * cwidth + cx] = new Cluster(); - }else{ - //reset data - for(var p : cluster.portals){ - p.clear(); - } - } - - PathCost cost = idToCost(pathCost); - - for(int direction = 0; direction < 4; direction++){ - int otherX = cx + Geometry.d4x(direction), otherY = cy + Geometry.d4y(direction); - //out of bounds, no portals in this direction - if(otherX < 0 || otherY < 0 || otherX >= cwidth || otherY >= cheight){ - continue; - } - - Cluster other = dim2[otherX + otherY * cwidth]; - IntSeq portals; - - if(other == null){ - //create new portals at direction - portals = cluster.portals[direction] = new IntSeq(4); - }else{ - //share portals with the other cluster - portals = cluster.portals[direction] = other.portals[(direction + 2) % 4]; - - //clear the portals, they're being recalculated now - portals.clear(); - } - - int addX = moveDirs[direction * 2], addY = moveDirs[direction * 2 + 1]; - int - baseX = cx * clusterSize + offsets[direction * 2] * (clusterSize - 1), - baseY = cy * clusterSize + offsets[direction * 2 + 1] * (clusterSize - 1), - nextBaseX = baseX + Geometry.d4[direction].x, - nextBaseY = baseY + Geometry.d4[direction].y; - - int lastPortal = -1; - boolean prevSolid = true; - - for(int i = 0; i < clusterSize; i++){ - int x = baseX + addX * i, y = baseY + addY * i; - - //scan for portals - if(solid(team, cost, x, y) || solid(team, cost, nextBaseX + addX * i, nextBaseY + addY * i)){ - int previous = i - 1; - //hit a wall, create portals between the two points - if(!prevSolid && previous >= lastPortal){ - //portals are an inclusive range - portals.add(Point2.pack(previous, lastPortal)); - } - prevSolid = true; - }else{ - //empty area encountered, mark the location of portal start - if(prevSolid){ - lastPortal = i; - } - prevSolid = false; - } - } - - //at the end of the loop, close any un-initialized portals; this is copy pasted code - int previous = clusterSize - 1; - if(!prevSolid && previous >= lastPortal){ - //portals are an inclusive range - portals.add(Point2.pack(previous, lastPortal)); - } - } - - updateInnerEdges(team, cost, cx, cy, cluster); - - return cluster; - } - - void updateInnerEdges(int team, int cost, int cx, int cy, Cluster cluster){ - updateInnerEdges(team, idToCost(cost), cx, cy, cluster); - } - - void updateInnerEdges(int team, PathCost cost, int cx, int cy, Cluster cluster){ - int minX = cx * clusterSize, minY = cy * clusterSize, maxX = Math.min(minX + clusterSize - 1, wwidth - 1), maxY = Math.min(minY + clusterSize - 1, wheight - 1); - - usedEdges.clear(); - - //clear all connections, since portals changed, they need to be recomputed. - cluster.portalConnections = new LongSeq[4][]; - - for(int direction = 0; direction < 4; direction++){ - var portals = cluster.portals[direction]; - if(portals == null) continue; - - int addX = moveDirs[direction * 2], addY = moveDirs[direction * 2 + 1]; - - for(int i = 0; i < portals.size; i++){ - usedEdges.add(Point2.pack(direction, i)); - - int - portal = portals.items[i], - from = Point2.x(portal), to = Point2.y(portal), - average = (from + to) / 2, - x = (addX * average + cx * clusterSize + offsets[direction * 2] * (clusterSize - 1)), - y = (addY * average + cy * clusterSize + offsets[direction * 2 + 1] * (clusterSize - 1)); - - for(int otherDir = 0; otherDir < 4; otherDir++){ - var otherPortals = cluster.portals[otherDir]; - if(otherPortals == null) continue; - - for(int j = 0; j < otherPortals.size; j++){ - - if(!usedEdges.contains(Point2.pack(otherDir, j))){ - - int - other = otherPortals.items[j], - otherFrom = Point2.x(other), otherTo = Point2.y(other), - otherAverage = (otherFrom + otherTo) / 2, - ox = cx * clusterSize + offsets[otherDir * 2] * (clusterSize - 1), - oy = cy * clusterSize + offsets[otherDir * 2 + 1] * (clusterSize - 1), - otherX = (moveDirs[otherDir * 2] * otherAverage + ox), - otherY = (moveDirs[otherDir * 2 + 1] * otherAverage + oy); - - //duplicate portal; should never happen. - if(Point2.pack(x, y) == Point2.pack(otherX, otherY)){ - continue; - } - - float connectionCost = innerAstar( - team, cost, - minX, minY, maxX, maxY, - x + y * wwidth, - otherX + otherY * wwidth, - (moveDirs[otherDir * 2] * otherFrom + ox), - (moveDirs[otherDir * 2 + 1] * otherFrom + oy), - (moveDirs[otherDir * 2] * otherTo + ox), - (moveDirs[otherDir * 2 + 1] * otherTo + oy) - ); - - if(connectionCost != -1f){ - if(cluster.portalConnections[direction] == null) cluster.portalConnections[direction] = new LongSeq[cluster.portals[direction].size]; - if(cluster.portalConnections[otherDir] == null) cluster.portalConnections[otherDir] = new LongSeq[cluster.portals[otherDir].size]; - if(cluster.portalConnections[direction][i] == null) cluster.portalConnections[direction][i] = new LongSeq(8); - if(cluster.portalConnections[otherDir][j] == null) cluster.portalConnections[otherDir][j] = new LongSeq(8); - - //TODO: can there be duplicate edges?? - cluster.portalConnections[direction][i].add(IntraEdge.get(otherDir, j, connectionCost)); - cluster.portalConnections[otherDir][j].add(IntraEdge.get(direction, i, connectionCost)); - } - } - } - } - } - } - } - - //distance heuristic: manhattan - private static float heuristic(int a, int b){ - int x = a % wwidth, x2 = b % wwidth, y = a / wwidth, y2 = b / wwidth; - return Math.abs(x - x2) + Math.abs(y - y2); - } - - private static int tcost(int team, PathCost cost, int tilePos){ - return cost.getCost(team, pathfinder.tiles[tilePos]); - } - - private static float tileCost(int team, PathCost type, int a, int b){ - //currently flat cost - return cost(team, type, b); - } - - /** @return -1 if no path was found */ - float innerAstar(int team, PathCost cost, int minX, int minY, int maxX, int maxY, int startPos, int goalPos, int goalX1, int goalY1, int goalX2, int goalY2){ - var frontier = innerFrontier; - var costs = innerCosts; - - frontier.clear(); - costs.clear(); - - //TODO: this can be faster and more memory efficient by making costs a NxN array... probably? - costs.put(startPos, 0); - frontier.add(startPos, 0); - - if(goalX2 < goalX1){ - int tmp = goalX1; - goalX1 = goalX2; - goalX2 = tmp; - } - - if(goalY2 < goalY1){ - int tmp = goalY1; - goalY1 = goalY2; - goalY2 = tmp; - } - - while(frontier.size > 0){ - int current = frontier.poll(); - - int cx = current % wwidth, cy = current / wwidth; - - //found the goal (it's in the portal rectangle) - if((cx >= goalX1 && cy >= goalY1 && cx <= goalX2 && cy <= goalY2) || current == goalPos){ - return costs.get(current); - } - - for(Point2 point : Geometry.d4){ - int newx = cx + point.x, newy = cy + point.y; - int next = newx + wwidth * newy; - - if(newx > maxX || newy > maxY || newx < minX || newy < minY || tcost(team, cost, next) == impassable) continue; - - float add = tileCost(team, cost, current, next); - - if(add < 0) continue; - - float newCost = costs.get(current) + add; - - if(newCost < costs.get(next, Float.POSITIVE_INFINITY)){ - costs.put(next, newCost); - float priority = newCost + heuristic(next, goalPos); - frontier.add(next, priority); - } - } - } - - return -1f; - } - - int makeNodeIndex(int cx, int cy, int dir, int portal){ - //to make sure there's only one way to refer to each node, the direction must be 0 or 1 (referring to portals on the top or right edge) - - //direction can only be 2 if cluster X is 0 (left edge of map) - if(dir == 2 && cx != 0){ - dir = 0; - cx --; - } - - //direction can only be 3 if cluster Y is 0 (bottom edge of map) - if(dir == 3 && cy != 0){ - dir = 1; - cy --; - } - - return NodeIndex.get(cx + cy * cwidth, dir, portal); - } - - //uses A* to find the closest node index to specified coordinates - //this node is used in cluster A* - /** @return MAX_VALUE if no node is found */ - private int findClosestNode(int team, int pathCost, int tileX, int tileY){ - int cx = tileX / clusterSize, cy = tileY / clusterSize; - - if(cx < 0 || cy < 0 || cx >= cwidth || cy >= cheight){ - return Integer.MAX_VALUE; - } - - PathCost cost = idToCost(pathCost); - Cluster cluster = getCreateCluster(team, pathCost, cx, cy); - int minX = cx * clusterSize, minY = cy * clusterSize, maxX = Math.min(minX + clusterSize - 1, wwidth - 1), maxY = Math.min(minY + clusterSize - 1, wheight - 1); - - int bestPortalPair = Integer.MAX_VALUE; - float bestCost = Float.MAX_VALUE; - - //A* to every node, find the best one (I know there's a better algorithm for this, probably dijkstra) - for(int dir = 0; dir < 4; dir++){ - var portals = cluster.portals[dir]; - if(portals == null) continue; - - for(int j = 0; j < portals.size; j++){ - - int - other = portals.items[j], - otherFrom = Point2.x(other), otherTo = Point2.y(other), - otherAverage = (otherFrom + otherTo) / 2, - ox = cx * clusterSize + offsets[dir * 2] * (clusterSize - 1), - oy = cy * clusterSize + offsets[dir * 2 + 1] * (clusterSize - 1), - otherX = (moveDirs[dir * 2] * otherAverage + ox), - otherY = (moveDirs[dir * 2 + 1] * otherAverage + oy); - - float connectionCost = innerAstar( - team, cost, - minX, minY, maxX, maxY, - tileX + tileY * wwidth, - otherX + otherY * wwidth, - (moveDirs[dir * 2] * otherFrom + ox), - (moveDirs[dir * 2 + 1] * otherFrom + oy), - (moveDirs[dir * 2] * otherTo + ox), - (moveDirs[dir * 2 + 1] * otherTo + oy) - ); - - //better cost found, update and return - if(connectionCost != -1f && connectionCost < bestCost){ - bestPortalPair = Point2.pack(dir, j); - bestCost = connectionCost; - } - } - } - - if(bestPortalPair != Integer.MAX_VALUE){ - return makeNodeIndex(cx, cy, Point2.x(bestPortalPair), Point2.y(bestPortalPair)); - } - - - return Integer.MAX_VALUE; - } - - //distance heuristic: manhattan - private float clusterNodeHeuristic(int team, int pathCost, int nodeA, int nodeB){ - int - clusterA = NodeIndex.cluster(nodeA), - dirA = NodeIndex.dir(nodeA), - portalA = NodeIndex.portal(nodeA), - clusterB = NodeIndex.cluster(nodeB), - dirB = NodeIndex.dir(nodeB), - portalB = NodeIndex.portal(nodeB), - rangeA = getCreateCluster(team, pathCost, clusterA).portals[dirA].items[portalA], - rangeB = getCreateCluster(team, pathCost, clusterB).portals[dirB].items[portalB]; - - float - averageA = (Point2.x(rangeA) + Point2.y(rangeA)) / 2f, - x1 = (moveDirs[dirA * 2] * averageA + (clusterA % cwidth) * clusterSize + offsets[dirA * 2] * (clusterSize - 1) + nextOffsets[dirA * 2] / 2f), - y1 = (moveDirs[dirA * 2 + 1] * averageA + (clusterA / cwidth) * clusterSize + offsets[dirA * 2 + 1] * (clusterSize - 1) + nextOffsets[dirA * 2 + 1] / 2f), - - averageB = (Point2.x(rangeB) + Point2.y(rangeB)) / 2f, - x2 = (moveDirs[dirB * 2] * averageB + (clusterB % cwidth) * clusterSize + offsets[dirB * 2] * (clusterSize - 1) + nextOffsets[dirB * 2] / 2f), - y2 = (moveDirs[dirB * 2 + 1] * averageB + (clusterB / cwidth) * clusterSize + offsets[dirB * 2 + 1] * (clusterSize - 1) + nextOffsets[dirB * 2 + 1] / 2f); - - return Math.abs(x1 - x2) + Math.abs(y1 - y2); - } - - @Nullable IntSeq clusterAstar(PathRequest request, int pathCost, int startNodeIndex, int endNodeIndex){ - var result = request.resultPath; - - if(startNodeIndex == endNodeIndex){ - result.clear(); - result.add(startNodeIndex); - return result; - } - - var team = request.team; - - if(request.costs == null) request.costs = new IntFloatMap(); - if(request.cameFrom == null) request.cameFrom = new IntIntMap(); - if(request.frontier == null) request.frontier = new PathfindQueue(); - - //note: these are NOT cleared, it is assumed that this function cleans up after itself at the end - //is this a good idea? don't know, might hammer the GC with unnecessary objects too - var costs = request.costs; - var cameFrom = request.cameFrom; - var frontier = request.frontier; - - cameFrom.put(startNodeIndex, startNodeIndex); - costs.put(startNodeIndex, 0); - frontier.add(startNodeIndex, 0); - - boolean foundEnd = false; - - while(frontier.size > 0){ - int current = frontier.poll(); - - if(current == endNodeIndex){ - foundEnd = true; - break; - } - - int cluster = NodeIndex.cluster(current), dir = NodeIndex.dir(current), portal = NodeIndex.portal(current); - int cx = cluster % cwidth, cy = cluster / cwidth; - Cluster clust = getCreateCluster(team, pathCost, cluster); - LongSeq innerCons = clust.portalConnections[dir] == null || portal >= clust.portalConnections[dir].length ? null : clust.portalConnections[dir][portal]; - - //edges for the cluster the node is 'in' - if(innerCons != null){ - checkEdges(request, team, pathCost, current, endNodeIndex, cx, cy, innerCons); - } - - //edges that this node 'faces' from the other side - int nextCx = cx + Geometry.d4[dir].x, nextCy = cy + Geometry.d4[dir].y; - if(nextCx >= 0 && nextCy >= 0 && nextCx < cwidth && nextCy < cheight){ - Cluster nextCluster = getCreateCluster(team, pathCost, nextCx, nextCy); - int relativeDir = (dir + 2) % 4; - LongSeq outerCons = nextCluster.portalConnections[relativeDir] == null ? null : nextCluster.portalConnections[relativeDir][portal]; - if(outerCons != null){ - checkEdges(request, team, pathCost, current, endNodeIndex, nextCx, nextCy, outerCons); - } - } - } - - //null them out, so they get GC'ed later - //there's no reason to keep them around and waste memory, since this path may never be recalculated - request.costs = null; - request.cameFrom = null; - request.frontier = null; - - if(foundEnd){ - result.clear(); - - int cur = endNodeIndex; - while(cur != startNodeIndex){ - result.add(cur); - cur = cameFrom.get(cur); - } - - result.reverse(); - - return result; - } - return null; - } - - private void checkEdges(PathRequest request, int team, int pathCost, int current, int goal, int cx, int cy, LongSeq connections){ - for(int i = 0; i < connections.size; i++){ - long con = connections.items[i]; - float cost = IntraEdge.cost(con); - int otherDir = IntraEdge.dir(con), otherPortal = IntraEdge.portal(con); - int next = makeNodeIndex(cx, cy, otherDir, otherPortal); - - float newCost = request.costs.get(current) + cost; - - if(newCost < request.costs.get(next, Float.POSITIVE_INFINITY)){ - request.costs.put(next, newCost); - - request.frontier.add(next, newCost + clusterNodeHeuristic(team, pathCost, next, goal)); - request.cameFrom.put(next, current); - } - } - } - - private void updateFields(FieldCache cache, long nsToRun){ - var frontier = cache.frontier; - var fields = cache.fields; - var goalPos = cache.goalPos; - var pcost = cache.cost; - var team = cache.team; - - long start = Time.nanos(); - int counter = 0; - - //actually do the flow field part - while(frontier.size > 0){ - int tile = frontier.removeLast(); - int baseX = tile % wwidth, baseY = tile / wwidth; - int curWeightIndex = (baseX / clusterSize) + (baseY / clusterSize) * cwidth; - - //TODO: how can this be null??? serious problem! - int[] curWeights = fields.get(curWeightIndex); - if(curWeights == null) continue; - - int cost = curWeights[baseX % clusterSize + ((baseY % clusterSize) * clusterSize)]; - - if(cost != impassable){ - for(Point2 point : Geometry.d4){ - - int - dx = baseX + point.x, dy = baseY + point.y, - clx = dx / clusterSize, cly = dy / clusterSize; - - if(clx < 0 || cly < 0 || dx >= wwidth || dy >= wheight) continue; - - int nextWeightIndex = clx + cly * cwidth; - - int[] weights = nextWeightIndex == curWeightIndex ? curWeights : fields.get(nextWeightIndex); - - //out of bounds; not allowed to move this way because no weights were registered here - if(weights == null) continue; - - int newPos = tile + point.x + point.y * wwidth; - - //can't move back to the goal - if(newPos == goalPos) continue; - - if(dx - clx * clusterSize < 0 || dy - cly * clusterSize < 0) continue; - - int newPosArray = (dx - clx * clusterSize) + (dy - cly * clusterSize) * clusterSize; - - int otherCost = pcost.getCost(team, pathfinder.tiles[newPos]); - int oldCost = weights[newPosArray]; - - //a cost of 0 means uninitialized, OR it means we're at the goal position, but that's handled above - if((oldCost == 0 || oldCost > cost + otherCost) && otherCost != impassable){ - frontier.addFirst(newPos); - weights[newPosArray] = cost + otherCost; - } - } - } - - //every N iterations, check the time spent - this prevents extra calls to nano time, which itself is slow - if(nsToRun >= 0 && (counter++) >= updateStepInterval){ - counter = 0; - if(Time.timeSinceNanos(start) >= nsToRun){ - return; - } - } - } - } - - private void addFlowCluster(FieldCache cache, int cluster, boolean addingFrontier){ - addFlowCluster(cache, cluster % cwidth, cluster / cwidth, addingFrontier); - } - - private void addFlowCluster(FieldCache cache, int cx, int cy, boolean addingFrontier){ - //out of bounds - if(cx < 0 || cy < 0 || cx >= cwidth || cy >= cheight) return; - - var fields = cache.fields; - int key = cx + cy * cwidth; - - if(!fields.containsKey(key)){ - fields.put(key, new int[clusterSize * clusterSize]); - - if(addingFrontier){ - for(int dir = 0; dir < 4; dir++){ - int ox = cx + nextOffsets[dir * 2], oy = cy + nextOffsets[dir * 2 + 1]; - - if(ox < 0 || oy < 0 || ox >= cwidth || ox >= cheight) continue; - - var otherField = cache.fields.get(ox + oy * cwidth); - - if(otherField == null) continue; - - int - relOffset = (dir + 2) % 4, - movex = moveDirs[relOffset * 2], - movey = moveDirs[relOffset * 2 + 1], - otherx1 = offsets[relOffset * 2] * (clusterSize - 1), - othery1 = offsets[relOffset * 2 + 1] * (clusterSize - 1); - - //scan the edge of the cluster - for(int i = 0; i < clusterSize; i++){ - int x = otherx1 + movex * i, y = othery1 + movey * i; - - //check to make sure it's not 0 (uninitialized flowfield data) - if(otherField[x + y * clusterSize] > 0){ - int worldX = x + ox * clusterSize, worldY = y + oy * clusterSize; - - //add the world-relative position to the frontier, so it recalculates - cache.frontier.addFirst(worldX + worldY * wwidth); - - if(debug){ - Core.app.post(() -> Fx.placeBlock.at(worldX *tilesize, worldY * tilesize, 1f)); - } - } - } - } - } - } - } - - private void initializePathRequest(PathRequest request, int team, int costId, int unitX, int unitY, int goalX, int goalY){ - PathCost pcost = idToCost(costId); - - int goalPos = (goalX + goalY * wwidth); - - int node = findClosestNode(team, costId, unitX, unitY); - int dest = findClosestNode(team, costId, goalX, goalY); - - if(dest == Integer.MAX_VALUE){ - request.notFound = true; - //no node found (TODO: invalid state??) - return; - } - - var nodePath = clusterAstar(request, costId, node, dest); - - FieldCache cache = fields.get(Pack.longInt(goalPos, costId)); - //if true, extra values are added on the sides of existing field cells that face new cells. - boolean addingFrontier = true; - - //create the cache if it doesn't exist, and initialize it - if(cache == null){ - cache = new FieldCache(pcost, costId, team, goalPos); - fields.put(cache.mapKey, cache); - FieldCache fcache = cache; - //register field in main thread for iteration - Core.app.post(() -> fieldList.add(fcache)); - cache.frontier.addFirst(goalPos); - addingFrontier = false; //when it's a new field, there is no need to add to the frontier to merge the flowfield - } - - if(nodePath != null){ - int cx = unitX / clusterSize, cy = unitY / clusterSize; - - addFlowCluster(cache, cx, cy, addingFrontier); - - for(int i = -1; i < nodePath.size; i++){ - int - current = i == -1 ? node : nodePath.items[i], - cluster = NodeIndex.cluster(current), - dir = NodeIndex.dir(current), - dx = Geometry.d4[dir].x, - dy = Geometry.d4[dir].y, - ox = cluster % cwidth + dx, - oy = cluster / cwidth + dy; - - addFlowCluster(cache, cluster, addingFrontier); - - //store directional/flipped version of cluster - if(ox >= 0 && oy >= 0 && ox < cwidth && oy < cheight){ - int other = ox + oy * cwidth; - - addFlowCluster(cache, other, addingFrontier); - } - } - } - } - - private PathCost idToCost(int costId){ - return ControlPathfinder.costTypes.get(costId); - } - - public boolean getPathPosition(Unit unit, Vec2 destination, Vec2 mainDestination, Vec2 out, @Nullable boolean[] noResultFound){ - int costId = unit.type.pathCostId; - PathCost cost = idToCost(costId); - - int - team = unit.team.id, - tileX = unit.tileX(), - tileY = unit.tileY(), - packedPos = world.packArray(tileX, tileY), - destX = World.toTile(mainDestination.x), - destY = World.toTile(mainDestination.y), - actualDestX = World.toTile(destination.x), - actualDestY = World.toTile(destination.y), - destPos = destX + destY * wwidth; - - PathRequest request = unitRequests.get(unit); - - unit.hitboxTile(Tmp.r3); - //tile rect size has tile size factored in, since the ray cannot have thickness - float tileRectSize = tilesize + Tmp.r3.height; - - int lastRaycastTile = request == null || world.tileChanges != request.lastWorldUpdate ? -1 : request.lastRaycastTile; - boolean raycastResult = request != null && request.lastRaycastResult; - - //cache raycast results to run every time the world updates, and every tile the unit crosses - if(lastRaycastTile != packedPos){ - //near the destination, standard raycasting tends to break down, so use the more permissive 'near' variant that doesn't take into account edges of walls - raycastResult = unit.within(destination, tilesize * 2.5f) ? !raycastRect(unit.x, unit.y, destination.x, destination.y, team, cost, tileX, tileY, actualDestX, actualDestY, tileRectSize) : !raycast(team, cost, tileX, tileY, actualDestX, actualDestY); - - if(request != null){ - request.lastRaycastTile = packedPos; - request.lastRaycastResult = raycastResult; - request.lastWorldUpdate = world.tileChanges; - } - } - - //if the destination can be trivially reached in a straight line, do that. - if(raycastResult){ - out.set(destination); - return true; - } - - boolean any = false; - - long fieldKey = Pack.longInt(destPos, costId); - - //use existing request if it exists. - if(request != null && request.destination == destPos){ - request.lastUpdateId = state.updateId; - - Tile tileOn = unit.tileOn(), initialTileOn = tileOn; - //TODO: should fields be accessible from this thread? - FieldCache fieldCache = fields.get(fieldKey); - - if(fieldCache != null && tileOn != null){ - FieldCache old = request.oldCache; - //nullify the old field to be GCed, as it cannot be relevant anymore (this path is complete) - if(fieldCache.frontier.isEmpty() && old != null){ - request.oldCache = null; - } - - fieldCache.lastUpdateId = state.updateId; - int maxIterations = 30; //TODO higher/lower number? is this still too slow? - int i = 0; - boolean recalc = false; - - //TODO last pos can change if the flowfield changes. - if(initialTileOn.pos() != request.lastTile || request.lastTargetTile == null){ - boolean anyNearSolid = false; - - //find the next tile until one near a solid block is discovered - while(i ++ < maxIterations){ - int value = getCost(fieldCache, old, tileOn.x, tileOn.y); - - Tile current = null; - int minCost = 0; - for(int dir = 0; dir < 4; dir ++){ - Point2 point = Geometry.d4[dir]; - int dx = tileOn.x + point.x, dy = tileOn.y + point.y; - - Tile other = world.tile(dx, dy); - - if(other == null) continue; - - int packed = world.packArray(dx, dy); - int otherCost = getCost(fieldCache, old, dx, dy), relCost = otherCost - value; - - if(relCost > 2 || otherCost <= 0){ - anyNearSolid = true; - } - - if((value == 0 || otherCost < value) && otherCost != impassable && (otherCost != 0 || packed == destPos) && (current == null || otherCost < minCost) && passable(unit.team.id, cost, packed)){ - current = other; - minCost = otherCost; - } - } - - //TODO raycast spam = extremely slow - //...flowfield integration spam is also really slow. - if(!(current == null || (costId == costGround && current.dangerous() && !tileOn.dangerous()))){ - - //when anyNearSolid is false, no solid tiles have been encountered anywhere so far, so raycasting is a waste of time - if(anyNearSolid && !tileOn.dangerous() && raycastRect(unit.x, unit.y, current.x * tilesize, current.y * tilesize, team, cost, initialTileOn.x, initialTileOn.y, current.x, current.y, tileRectSize)){ - - //TODO this may be a mistake - if(tileOn == initialTileOn){ - recalc = true; - any = true; - } - - break; - }else{ - tileOn = current; - any = true; - - if(current.array() == destPos){ - break; - } - } - - }else{ - break; - } - } - - request.lastTargetTile = any ? tileOn : null; - if(debug && tileOn != null){ - Fx.placeBlock.at(tileOn.worldx(), tileOn.worldy(), 1); - } - } - - if(request.lastTargetTile != null){ - out.set(request.lastTargetTile); - request.lastTile = recalc ? -1 : initialTileOn.pos(); - return true; - } - } - }else if(request == null){ - - //queue new request. - unitRequests.put(unit, request = new PathRequest(unit, team, costId, destPos)); - - PathRequest f = request; - - //on the pathfinding thread: initialize the request - queue.post(() -> { - threadPathRequests.add(f); - recalculatePath(f); - }); - - out.set(destination); - - return true; - } - - if(noResultFound != null){ - noResultFound[0] = request.notFound; - } - return false; - } - - private void recalculatePath(PathRequest request){ - initializePathRequest(request, request.team, request.costId, request.unit.tileX(), request.unit.tileY(), request.destination % wwidth, request.destination / wwidth); - } - - private int getCost(FieldCache cache, FieldCache old, int x, int y){ - //fall back to the old flowfield when possible - it's best not to use partial results from the base cache - if(old != null){ - return getCost(old, x, y, false); - } - return getCost(cache, x, y, true); - } - - private int getCost(FieldCache cache, int x, int y, boolean requeue){ - int[] field = cache.fields.get(x / clusterSize + (y / clusterSize) * cwidth); - if(field == null){ - if(!requeue) return 0; - //request a new flow cluster if one wasn't found; this may be a spammed a bit, but the function will return early once it's created the first time - queue.post(() -> addFlowCluster(cache, x / clusterSize, y / clusterSize, true)); - return 0; - } - return field[(x % clusterSize) + (y % clusterSize) * clusterSize]; - } - - private static boolean raycast(int team, PathCost type, int x1, int y1, int x2, int y2){ - int ww = wwidth, wh = wheight; - int x = x1, dx = Math.abs(x2 - x), sx = x < x2 ? 1 : -1; - int y = y1, dy = Math.abs(y2 - y), sy = y < y2 ? 1 : -1; - int e2, err = dx - dy; - - while(x >= 0 && y >= 0 && x < ww && y < wh){ - if(avoid(team, type, x + y * wwidth)) return true; - if(x == x2 && y == y2) return false; - - //diagonal ver - e2 = 2 * err; - if(e2 > -dy){ - err -= dy; - x += sx; - } - - if(e2 < dx){ - err += dx; - y += sy; - } - } - - return true; - } - - private static boolean raycastNear(int team, PathCost type, int x1, int y1, int x2, int y2){ - int ww = wwidth, wh = wheight; - int x = x1, dx = Math.abs(x2 - x), sx = x < x2 ? 1 : -1; - int y = y1, dy = Math.abs(y2 - y), sy = y < y2 ? 1 : -1; - int err = dx - dy; - - - while(x >= 0 && y >= 0 && x < ww && y < wh){ - if(!passable(team, type, x + y * wwidth)) return true; - if(x == x2 && y == y2) return false; - - //no diagonal ver - if(2 * err + dy > dx - 2 * err){ - err -= dy; - x += sx; - }else{ - err += dx; - y += sy; - } - - } - - return true; - } - - private static boolean overlap(int team, PathCost type, int x, int y, float startX, float startY, float endX, float endY, float rectSize){ - if(x < 0 || y < 0 || x >= wwidth || y >= wheight) return false; - if(!passable(team, type, x + y * wwidth)){ - return Intersector.intersectSegmentRectangleFast(startX, startY, endX, endY, x * tilesize - rectSize/2f, y * tilesize - rectSize/2f, rectSize, rectSize); - } - return false; - } - - private static boolean raycastRect(float startX, float startY, float endX, float endY, int team, PathCost type, int x1, int y1, int x2, int y2, float rectSize){ - int ww = wwidth, wh = wheight; - int x = x1, dx = Math.abs(x2 - x), sx = x < x2 ? 1 : -1; - int y = y1, dy = Math.abs(y2 - y), sy = y < y2 ? 1 : -1; - int e2, err = dx - dy; - - while(x >= 0 && y >= 0 && x < ww && y < wh){ - if( - !passable(team, type, x + y * wwidth) || - overlap(team, type, x + 1, y, startX, startY, endX, endY, rectSize) || - overlap(team, type, x - 1, y, startX, startY, endX, endY, rectSize) || - overlap(team, type, x, y + 1, startX, startY, endX, endY, rectSize) || - overlap(team, type, x, y - 1, startX, startY, endX, endY, rectSize) - ) return true; - - if(x == x2 && y == y2) return false; - - //diagonal ver - e2 = 2 * err; - if(e2 > -dy){ - err -= dy; - x += sx; - } - - if(e2 < dx){ - err += dx; - y += sy; - } - } - - return true; - } - - private static boolean avoid(int team, PathCost type, int tilePos){ - int cost = cost(team, type, tilePos); - return cost == impassable || cost >= 2; - } - - private static boolean passable(int team, PathCost cost, int pos){ - int amount = cost.getCost(team, pathfinder.tiles[pos]); - //edge case: naval reports costs of 6000+ for non-liquids, even though they are not technically passable - return amount != impassable && !(cost == costTypes.get(costNaval) && amount >= 6000); - } - - private static boolean solid(int team, PathCost type, int x, int y){ - return x < 0 || y < 0 || x >= wwidth || y >= wheight || solid(team, type, x + y * wwidth, true); - } - - private static boolean solid(int team, PathCost type, int tilePos, boolean checkWall){ - int cost = cost(team, type, tilePos); - return cost == impassable || (checkWall && cost >= 6000); - } - - private static int cost(int team, PathCost cost, int tilePos){ - if(state.rules.limitMapArea && !Team.get(team).isAI()){ - int x = tilePos % wwidth, y = tilePos / wwidth; - if(x < state.rules.limitX || y < state.rules.limitY || x > state.rules.limitX + state.rules.limitWidth || y > state.rules.limitY + state.rules.limitHeight){ - return impassable; - } - } - return cost.getCost(team, pathfinder.tiles[tilePos]); - } - - private void clusterChanged(int team, int pathCost, int cx, int cy){ - int index = cx + cy * cwidth; - - for(var req : threadPathRequests){ - long mapKey = Pack.longInt(req.destination, pathCost); - var field = fields.get(mapKey); - if((field != null && field.fields.containsKey(index)) || req.notFound){ - invalidRequests.add(req); - } - } - - } - - private void updateClustersComplete(int clusterIndex){ - for(int team = 0; team < clusters.length; team++){ - var dim1 = clusters[team]; - if(dim1 != null){ - for(int pathCost = 0; pathCost < dim1.length; pathCost++){ - var dim2 = dim1[pathCost]; - if(dim2 != null){ - var cluster = dim2[clusterIndex]; - if(cluster != null){ - updateCluster(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth); - clusterChanged(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth); - } - } - } - } - } - } - - private void updateClustersInner(int clusterIndex){ - for(int team = 0; team < clusters.length; team++){ - var dim1 = clusters[team]; - if(dim1 != null){ - for(int pathCost = 0; pathCost < dim1.length; pathCost++){ - var dim2 = dim1[pathCost]; - if(dim2 != null){ - var cluster = dim2[clusterIndex]; - if(cluster != null){ - updateInnerEdges(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth, cluster); - clusterChanged(team, pathCost, clusterIndex % cwidth, clusterIndex / cwidth); - } - } - } - } - } - } - - @Override - public void run(){ - long lastInvalidCheck = Time.millis() + invalidateCheckInterval; - - while(true){ - if(net.client()) return; - try{ - - - if(state.isPlaying()){ - queue.run(); - - clustersToUpdate.each(cluster -> { - updateClustersComplete(cluster); - - //just in case: don't redundantly update inner clusters after you've recalculated it entirely - clustersToInnerUpdate.remove(cluster); - }); - - clustersToInnerUpdate.each(cluster -> { - //only recompute the inner links - updateClustersInner(cluster); - }); - - clustersToInnerUpdate.clear(); - clustersToUpdate.clear(); - - //periodically check for invalidated paths - if(Time.timeSinceMillis(lastInvalidCheck) > invalidateCheckInterval){ - lastInvalidCheck = Time.millis(); - - var it = invalidRequests.iterator(); - while(it.hasNext()){ - var request = it.next(); - - //invalid request, ignore it - if(request.invalidated){ - it.remove(); - continue; - } - - long mapKey = Pack.longInt(request.destination, request.costId); - - var field = fields.get(mapKey); - - if(field != null){ - //it's only worth recalculating a path when the current frontier has finished; otherwise the unit will be following something incomplete. - if(field.frontier.isEmpty()){ - - //remove the field, to be recalculated next update one recalculatePath is processed - fields.remove(field.mapKey); - Core.app.post(() -> fieldList.remove(field)); - - //once the field is invalidated, make sure that all the requests that have it stored in their 'old' field, so units don't stutter during recalculations - for(var otherRequest : threadPathRequests){ - if(otherRequest.destination == request.destination){ - otherRequest.oldCache = field; - } - } - - //the recalculation is done next update, so multiple path requests in the same batch don't end up removing and recalculating the field multiple times. - queue.post(() -> recalculatePath(request)); - //it has been processed. - it.remove(); - } - }else{ //there's no field, presumably because a previous request already invalidated it. - queue.post(() -> recalculatePath(request)); - it.remove(); - } - } - } - - //each update time (not total!) no longer than maxUpdate - for(FieldCache cache : fields.values()){ - updateFields(cache, maxUpdate); - } - } - - try{ - Thread.sleep(updateInterval); - }catch(InterruptedException e){ - //stop looping when interrupted externally - return; - } - }catch(Throwable e){ - e.printStackTrace(); - } - } - } - - @Struct - static class IntraEdgeStruct{ - @StructField(8) - int dir; - @StructField(8) - int portal; - - float cost; - } - - @Struct - static class NodeIndexStruct{ - @StructField(22) - int cluster; - @StructField(2) - int dir; - @StructField(8) - int portal; - } -} diff --git a/core/src/mindustry/ai/types/CommandAI.java b/core/src/mindustry/ai/types/CommandAI.java index 07a30878b1..dec38c14ed 100644 --- a/core/src/mindustry/ai/types/CommandAI.java +++ b/core/src/mindustry/ai/types/CommandAI.java @@ -4,7 +4,6 @@ import arc.math.*; import arc.math.geom.*; import arc.struct.*; import arc.util.*; -import mindustry.*; import mindustry.ai.*; import mindustry.core.*; import mindustry.entities.*; @@ -35,7 +34,6 @@ public class CommandAI extends AIController{ protected boolean stopAtTarget, stopWhenInRange; protected Vec2 lastTargetPos; - protected int pathId = -1; protected boolean blockingUnit; protected float timeSpentBlocked; @@ -251,7 +249,7 @@ public class CommandAI extends AIController{ timeSpentBlocked = 0f; } - move = hpath.getPathPosition(unit, vecMovePos, targetPos, vecOut, noFound) && (!blockingUnit || timeSpentBlocked > maxBlockTime); + move = controlPath.getPathPosition(unit, vecMovePos, targetPos, vecOut, noFound) && (!blockingUnit || timeSpentBlocked > maxBlockTime); //rare case where unit must be perfectly aligned (happens with 1-tile gaps) alwaysArrive = vecOut.epsilonEquals(unit.tileX() * tilesize, unit.tileY() * tilesize); //we've reached the final point if the returned coordinate is equal to the supplied input @@ -421,7 +419,6 @@ public class CommandAI extends AIController{ //this is an allocation, but it's relatively rarely called anyway, and outside mutations must be prevented targetPos = lastTargetPos = pos.cpy(); attackTarget = null; - pathId = Vars.controlPath.nextTargetId(); this.stopWhenInRange = stopWhenInRange; } @@ -436,7 +433,6 @@ public class CommandAI extends AIController{ public void commandTarget(Teamc moveTo, boolean stopAtTarget){ attackTarget = moveTo; this.stopAtTarget = stopAtTarget; - pathId = Vars.controlPath.nextTargetId(); } /* diff --git a/core/src/mindustry/ai/types/LogicAI.java b/core/src/mindustry/ai/types/LogicAI.java index 2ac8840d3f..9486ebea42 100644 --- a/core/src/mindustry/ai/types/LogicAI.java +++ b/core/src/mindustry/ai/types/LogicAI.java @@ -85,7 +85,7 @@ public class LogicAI extends AIController{ if(unit.isFlying()){ moveTo(Tmp.v1.set(moveX, moveY), 1f, 30f); }else{ - if(hpath.getPathPosition(unit, Tmp.v2.set(moveX, moveY), Tmp.v2, Tmp.v1, null)){ + if(controlPath.getPathPosition(unit, Tmp.v2.set(moveX, moveY), Tmp.v2, Tmp.v1, null)){ moveTo(Tmp.v1, 1f, Tmp.v2.epsilonEquals(Tmp.v1, 4.1f) ? 30f : 0f); } } From 4a0ce36e2e48a378b22de83b2720584a1d035d1d Mon Sep 17 00:00:00 2001 From: summoner001 Date: Thu, 18 Apr 2024 20:35:09 +0200 Subject: [PATCH 120/348] Update bundle_hu.properties (#9710) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update bundle_hu.properties Kisebb javítások, hogy könnyebben érthetőek legyenek a mondatok. * Update bundle_hu.properties More small fixes. * Update bundle_hu.properties minor mispells corrections * Update bundle_hu.properties correct a misspell * Update bundle_hu.properties Fixed snow name. Sorry, I just noticed that the text has changed. * Update bundle_hu.properties fixing a mispell * Update bundle_hu.properties Where buildings have icons in the text and are explicitly highlighted, I capitalize the names of the buildings everywhere. I have made the translation of pump more consistent, because in places it has been translated as "pumpa" instead of szivattyú. * Update bundle_hu.properties Where buildings have icons in the text and are explicitly highlighted, I capitalize the names of the buildings everywhere. I have made the translation of fabricators more consistent, because in places it has been translated as "-gyártó" instead of -gyár. * Update bundle_hu.properties Fix Repülőgépgyár * Update bundle_hu.properties Where buildings have icons in the text and are explicitly highlighted, I capitalize the names of the buildings everywhere. I have made the translation of fabricator more consistent, because in places it has been translated as "-gyár" instead of -gyártó. * Update bundle_hu.properties Fixing remarked misspells Pirolíziserőmű -> Pirolízis-erőmű * Update bundle_hu.properties Follow the english bundle.properties file changes. Minor fix. * Update bundle_hu.properties Fix ambiguous word translation. * Update bundle_hu.properties Following the english bundle. The added 2 new lines has been translated. * Update bundle_hu.properties Add a blank line to make the text follow the look of the english bundle. --- core/assets/bundles/bundle_hu.properties | 129 ++++++++++++----------- 1 file changed, 67 insertions(+), 62 deletions(-) diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index b04b225ac8..c839b306a1 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -46,7 +46,7 @@ mods.browser.selected = Mod kiválasztása mods.browser.add = Letöltés mods.browser.reinstall = Újratelepítés mods.browser.view-releases = Kiadások megtekintése -mods.browser.noreleases = [scarlet]Nem találhatóak a kiadások\n[accent]Nem találhatók kiadások ehhez a modhoz. Nézd meg a tárolóját, hogy vannak-e kiadásai. +mods.browser.noreleases = [scarlet]Nem találhatóak a kiadások\n[accent]Nem találhatóak kiadások ehhez a modhoz. Nézd meg a tárolóját, hogy vannak-e kiadásai. mods.browser.latest = [lightgray][Legújabb] mods.browser.releases = Kiadások mods.github.open = Tároló @@ -309,7 +309,7 @@ server.invalidport = Érvénytelen port! server.error = [scarlet]Kiszolgálási hiba. save.new = Új mentés save.overwrite = Biztosan felülírod\nezt a mentést? -save.nocampaign = A hadjáratból származó egyes mentési fájlok nem importálhatók. +save.nocampaign = A hadjáratból származó egyes mentési fájlok nem importálhatóak. overwrite = Felülírás save.none = Nem található mentés! savefail = Nem sikerült elmenteni a játékot! @@ -332,7 +332,7 @@ on = Be off = Ki save.search = Keresés a mentett játékok között... save.autosave = Automatikus mentés: {0} -save.map = Térkép: {0} +save.map = Pálya: {0} save.wave = Hullám: {0} save.mode = Játékmód: {0} save.date = Utolsó mentés: {0} @@ -614,7 +614,7 @@ filter.option.threshold2 = Másodlagos küszöbérték filter.option.radius = Sugár filter.option.percentile = Százalék -locales.info = Itt adhatsz hozzá különböző nyelvi csomagokat a pályádhoz. A nyelvi csomagokban minden tulajdonságnak van egy neve és egy értéke. Ezeket a tulajdonságokat a világfeldolgozók és a célkitűzések is használhatják a saját neveikkel. Támogatják a szövegformázást (a helyőrzőket a tényleges értékükkel helyettesítik).\n\n[cyan]Példa tulajdonság:\n[]name: [accent]időzítő[]\nvalue: [accent]Példa időzítő, hátralévő idő: {0}[]\n\n[cyan]Használat:\n[]Beállítás célkitűzés szövegeként: [accent]@időzítő\n\n[]Írd be egy világfeldolgozóba:\n[accent]localeprint "időzítő"\nformat time\n[gray](ahol az idő egy külön számított változó) +locales.info = Itt adhatsz hozzá különböző nyelvi csomagokat a pályádhoz. A nyelvi csomagokban minden tulajdonságnak van egy neve és egy értéke. Ezeket a tulajdonságokat a világprocesszorok és a célkitűzések is használhatják a saját neveikkel. Támogatják a szövegformázást (a helyőrzőket a tényleges értékükkel helyettesítik).\n\n[cyan]Példa tulajdonság:\n[]name: [accent]időzítő[]\nvalue: [accent]Példa időzítő, hátralévő idő: {0}[]\n\n[cyan]Használat:\n[]Beállítás célkitűzés szövegeként: [accent]@időzítő\n\n[]Írd be egy világprocesszorba:\n[accent]localeprint "időzítő"\nformat time\n[gray](ahol az idő egy külön számított változó) locales.deletelocale = Biztos, hogy törölni akarod ezt a nyelvi csomagot? locales.applytoall = Változások alkalmazása az összes nyelvi csomagra locales.addtoother = Hozzáadás más nyelvi csomagokhoz @@ -688,7 +688,7 @@ marker.shape.name = Alakzat marker.text.name = Szöveg marker.line.name = Vonal marker.quad.name = Négyzet -marker.texture.name = Texture +marker.texture.name = Textúra marker.background = Háttér marker.outline = Körvonal @@ -817,12 +817,12 @@ sector.navalFortress.name = Tengerészeti erőd sector.groundZero.description = Az ideális helyszín, hogy ismét belekezdjünk. Alacsony ellenséges fenyegetés. Kevés nyersanyag.\nGyűjts annyi rezet és ólmot, amennyit csak tudsz.\nHaladj tovább. sector.frozenForest.description = Még itt, a hegyekhez közel is elterjedtek a spórák. A fagypont alatti hőmérséklet nem tudja örökké fogva tartani őket.\n\nFedezd fel az elektromosság erejét! Építs égetőerőműveket! Tanuld meg a foltozók használatát! sector.saltFlats.description = A sivatag peremén terülnek el a Sós síkságok. Kevés nyersanyag található errefelé.\n\nAz ellenség egy raktárkomplexumot létesített itt. Pusztítsd el a támaszpontjukat! Kő kövön ne maradjon! -sector.craters.description = Víz gyűjt össze ebben a kráterben, amely régi háborúk emlékét őrzi. Szerezd vissza a területet. Gyűjts homokot! Olvassz üveget! Pumpálj vizet, hogy lehűtsd a fúróidat és lövegtornyaidat. +sector.craters.description = Víz gyűlt össze ebben a kráterben, amely régi háborúk emlékét őrzi. Szerezd vissza a területet. Gyűjts homokot! Olvassz üveget! Szivattyúzz vizet, hogy lehűtsd a fúróidat és lövegtornyaidat. sector.ruinousShores.description = A pusztaság mögött a partvonal húzódik. Valaha ezen a helyen egy partvédelmi rendszer állt. Nem sok minden maradt belőle. Csak a legalapvetőbb védelmi szerkezetek maradtak érintetlenül, minden más csak törmelék lett.\nFolytasd a terjeszkedést! Fedezd fel újra a technológiát! sector.stainedMountains.description = Mélyebben a szárazföldön fekszenek a hegyek, a spóráktól még érintetlenül.\nTermeld ki a bőséges titán készleteket a körzetben. Tanuld meg felhasználni!.\n\nAz ellenség itt nagyobb létszámban van jelen. Ne hagyj nekik időt, hogy a legerősebb egységeiket hadba állíthassák! sector.overgrowth.description = Ez a terület közelebb esik a spórák forrásához, a spórák már kinőtték.\nAz ellenség egy helyőrséget létesített itt. Építs Mace egységeket! Pusztítsd el a bázist! -sector.tarFields.description = Egy olajtermelő övezet peremvidéke a hegyek és a sivatag között. Egy azon kevés szektorok közül, ahol még hasznosítható kátránykészletek találhatók.\nBár a terület elhagyatott, veszélyes ellenséges erők fészkelnek a közelben. Ne becsüld alá őket!\n\n[lightgray]Fedezd fel az olajfeldolgozási lehetőségeket, ha tudod! -sector.desolateRift.description = Egy extrém veszélyes zóna. Nyersanyagokban gazdag, de szűkös a hely. Magas a kockázat. Építsd szárazföldi és légvédelmet, amint csak tudsz. Ne tévesszen meg a hosszú szünet az ellenség támadásai között. +sector.tarFields.description = Egy olajtermelő övezet peremvidéke a hegyek és a sivatag között. Egy azon kevés szektorok közül, ahol még hasznosítható kátránykészletek találhatóak.\nBár a terület elhagyatott, veszélyes ellenséges erők fészkelnek a közelben. Ne becsüld alá őket!\n\n[lightgray]Fedezd fel az olajfeldolgozási lehetőségeket, ha tudod! +sector.desolateRift.description = Egy extrém veszélyes zóna. Nyersanyagokban gazdag, de szűkös a hely. Magas a kockázat. Építs szárazföldi és légvédelmet, amint csak tudsz. Ne tévesszen meg a hosszú szünet az ellenség támadásai között. sector.nuclearComplex.description = Egy néhai tóriumkitermelő és feldolgozó létesítmény, romokban.\n[lightgray]Fedezd fel a tóriumot és a sokrétű felhasználását!\n\nAz ellenség nagy létszámban van jelen, és folyamatosan megfigyelés alatt tartják a környéket. sector.fungalPass.description = Átmeneti terület a magas hegyek és a mélyebben fekvő, spórák uralta lapály között. Egy kisebb ellenséges megfigyelő állomás található itt.\nSemmisítsd meg!\nHasználj Dagger és Crawler egységeket! Pusztítsd el a két támaszpontot! sector.biomassFacility.description = A spórák származási helye. Ebben a létesítményben fejlesztették ki őket, és eredetileg itt is gyártották őket.\nFedezd fel az itt található technológiákat. Tenyészd ki a spórákat üzemanyag és műanyagok gyártásához.\n\n[lightgray]A létesítmény pusztulása nyomán a spórák elszabadultak és szétszóródtak a légkörben. A helyi ökoszisztémában semmi sem tudta felvenni a versenyt egy ennyire invazív életformával. @@ -853,9 +853,9 @@ sector.origin.name = Eredet sector.onset.description = Kezdd meg az Erekir meghódítását. Gyűjts nyersanyagokat, állíts elő egységeket, és kezdd el a technológiai fejlesztéseket. sector.aegis.description = Ez a szektor volfrám-lelőhelyeket tartalmaz.\nFejleszd ki az [accent]Ütvefúrót[], hogy ki tudd bányászni ezt a nyersanyagot, és pusztítsd el az ellenséges bázist a szektorban. -sector.lake.description = Az ebben a szektorban lévő salakos tó nagymértékben korlátozza a használható egységeket. A lebegőegységek használata az egyetlen lehetőség.\nFejleszd ki a [accent]Repülőgépgyárat[], és állíts elő egy [accent]Elude[] egységet, amilyen hamar csak lehet. +sector.lake.description = Az ebben a szektorban lévő salakos tó nagymértékben korlátozza a használható egységeket. A lebegőegységek használata az egyetlen lehetőség.\nFejleszd ki a [accent]Repülőgépgyártót[], és állíts elő egy [accent]Elude[] egységet, amilyen hamar csak lehet. sector.intersect.description = A letapogatások arra utalnak, hogy ezt a szektort a leszállás után hamarosan több oldalról is megtámadják.\nÁllítsd fel gyorsan a védelmedet, és terjeszkedj minél hamarabb.\n[accent]Mech[] egységekre lesz szükség a terület zord terepviszonyai miatt. -sector.atlas.description = Ez a szektor változatos terepet tartalmaz, és az ütőképes támadáshoz többféle egységre lesz szükség.\nAz itt felfedezett ellenséges bázisok némelyikén való átjutáshoz is továbbfejlesztett egységekre lehet szükség.\nFejleszd ki az [accent]Elektrolizátort[] és a [accent]Tank újratervezőt[]. +sector.atlas.description = Ez a szektor változatos terepet tartalmaz, és az ütőképes támadáshoz többféle egységre lesz szükség.\nAz itt felfedezett ellenséges bázisok némelyikén való átjutáshoz is továbbfejlesztett egységekre lehet szükség.\nFejleszd ki az [accent]Elektrolizátort[] és a [accent]Tankújratervezőt[]. sector.split.description = A minimális ellenséges jelenlét miatt ez a szektor tökéletes az új nyersanyagszállító technológiák tesztelésére. sector.basin.description = Jelentős ellenséges jelenlét lett érzékelve ebben a szektorban.\nÉpíts gyorsan egységeket, és foglald el az ellenséges támaszpontokat, hogy megvethesd a lábad. sector.marsh.description = Ebben a szektorban rengeteg arkicit található, de kevés a kürtő.\nÉpíts [accent]Kémiai égetőkamrát[] az áramfejlesztéshez. @@ -863,8 +863,8 @@ sector.peaks.description = A hegyvidéki terep ebben a szektorban a legtöbb egy sector.ravine.description = A szektorban nem észlelhető ellenséges támaszpont, de ez egy fontos szállítási útvonal az ellenség számára, így változatos ellenséges erőkkel kell számolni.\nTermelj [accent]elektrometált[]. Építs [accent]Afflict[] lövegtornyokat. sector.caldera-erekir.description = Ebben a szektorban a feltárható nyersanyagok több szigeten szétszóródva találhatóak.\nFejleszd ki és helyezd üzembe a drónalapú szállítmányozást. sector.stronghold.description = A nagy ellenséges tábor ebben a szektorban jelentős mennyiségű [accent]tóriumot[] őriz.\nHasználd magasabb szintű egységek és lövegtornyok fejlesztésére. -sector.crevice.description = Ebben a szektorban az ellenség kegyetlen támadóerőket fog mozgósítani, hogy kiiktassa a bázisodat.\nA [accent]karbid[] és a [accent]Pirolízis erőmű[] kifejlesztése nélkülözhetetlen lehet a túléléshez. -sector.siege.description = Ebben a szektorban két párhuzamos kanyon található, amelyek két irányból érkező támadásokat tesznek lehetővé.\nFejleszd ki a [accent]diciánt[], hogy még erősebb tankegységeket hozhass létre.\nVigyázat: ellenséges, nagy hatótávolságú rakéták észlelve. A rakéták a becsapódásuk előtt megsemmisíthetők. +sector.crevice.description = Ebben a szektorban az ellenség kegyetlen támadóerőket fog mozgósítani, hogy kiiktassa a bázisodat.\nA [accent]karbid[] és a [accent]Pirolízis-erőmű[] kifejlesztése nélkülözhetetlen lehet a túléléshez. +sector.siege.description = Ebben a szektorban két párhuzamos kanyon található, amelyek két irányból érkező támadásokat tesznek lehetővé.\nFejleszd ki a [accent]diciánt[], hogy még erősebb tankegységeket hozhass létre.\nVigyázat: ellenséges, nagy hatótávolságú rakéták észlelve. A rakéták a becsapódásuk előtt megsemmisíthetőek. sector.crossroads.description = Az ellenséges támaszpontok ebben a szektorban változó terepviszonyok között alakultak ki. Ahhoz, hogy alkalmazkodni tudj, fejlessz ki különböző egységeket.\nEzenkívül egyes bázisokat pajzsok védenek. Találd ki, hogyan táplálják őket. sector.karst.description = Ez a szektor gazdag a nyersanyagokban, de amint egy új támaszpont leszáll, az ellenség megtámadja azt.\nHasználd ki a nyersanyagokat és fedezd fel a [accent]tóritkvarcot[]. sector.origin.description = Az utolsó szektor, jelentős ellenséges jelenléttel.\nNem valószínű, hogy maradtak további fejlesztési lehetőségek – koncentrálj az ellenséges támaszpontok elpusztítására. @@ -955,7 +955,7 @@ stat.moduletier = Modul szintje stat.unittype = Egység típusa stat.speedincrease = Gyorsítás stat.range = Hatótáv -stat.drilltier = Kitermelhetők +stat.drilltier = Kitermelhetőek stat.drillspeed = Alap termelési sebesség stat.boosteffect = Erősítés hatása stat.maxunits = Max. aktív egységek @@ -1027,6 +1027,7 @@ ability.spawndeath = Szétesés ability.spawndeath.description = Megsemmisülésekor egységeket bocsát ki ability.liquidexplode = Szétömlés ability.liquidexplode.description = Megsemmisülésekor folyadék ömlik ki belőle + ability.stat.firingrate = [stat]{0}/mp[lightgray] tüzelési sebesség ability.stat.regen = [stat]{0}[lightgray] életerő/mp ability.stat.shield = [stat]{0}[lightgray] pajzs @@ -1080,7 +1081,7 @@ bullet.homing = [stat]nyomkövető bullet.armorpierce = [stat]páncéltörő bullet.maxdamagefraction = [stat]{0}%[lightgray] sebzési határérték bullet.suppression = [stat]{0} mp[lightgray] javításelnyomás ~[stat]{1}[lightgray] csempe -bullet.interval = [stat]{0}/mp[lightgray] lövedékek időköze: +bullet.interval = [stat]{0}/mp[lightgray] gyakoriságú lövedékek: bullet.frags = [stat]{0}[lightgray]x repeszlövedék: bullet.lightning = [stat]{0}[lightgray]x villámcsapás ~[stat]{1}[lightgray] sebzés bullet.buildingdamage = [stat]{0}%[lightgray] épületsebzés @@ -1115,7 +1116,7 @@ unit.thousands = k unit.millions = mil unit.billions = Mrd unit.pershot = /lövés -category.purpose = Cél +category.purpose = Rendeltetés category.general = Általános category.power = Áram category.liquids = Folyadékok @@ -1235,16 +1236,17 @@ keybind.unit_stance_hold_fire.name = Egység viselkedése: tüzet szüntess keybind.unit_stance_pursue_target.name = Egység viselkedése: célpont követése keybind.unit_stance_patrol.name = Egység viselkedése: járőrözés keybind.unit_stance_ram.name = Egység viselkedése: ütközés -keybind.unit_command_move.name = Unit Command: Move -keybind.unit_command_repair.name = Unit Command: Repair -keybind.unit_command_rebuild.name = Unit Command: Rebuild -keybind.unit_command_assist.name = Unit Command: Assist -keybind.unit_command_mine.name = Unit Command: Mine -keybind.unit_command_boost.name = Unit Command: Boost -keybind.unit_command_load_units.name = Unit Command: Load Units -keybind.unit_command_load_blocks.name = Unit Command: Load Blocks -keybind.unit_command_unload_payload.name = Unit Command: Unload Payload -keybind.unit_command_enter_payload.name = Unit Command: Enter Payload + +keybind.unit_command_move.name = Egységparancs: mozgás +keybind.unit_command_repair.name = Egységparancs: javítás +keybind.unit_command_rebuild.name = Egységparancs: újjáépítés +keybind.unit_command_assist.name = Egységparancs: támogatás +keybind.unit_command_mine.name = Egységparancs: bányászás +keybind.unit_command_boost.name = Egységparancs: erősítés +keybind.unit_command_load_units.name = Egységparancs: egységek berakodása +keybind.unit_command_load_blocks.name = Egységparancs: blokkok berakodása +keybind.unit_command_unload_payload.name = Egységparancs: kirakodás +keybind.unit_command_enter_payload.name = Egységparancs: berakodás keybind.rebuild_select.name = Régió újjáépítése keybind.schematic_select.name = Terület kijelölése @@ -1379,6 +1381,9 @@ rules.weather.duration = Időtartam: rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. +rules.placerangecheck.info = Megakadályozza, hogy a játékosok lövegtornyokat helyezzenek el az ellenséges épületek közelében. Amikor megpróbálnak egy lövegtornyot elhelyezni, az építési távolság megnő, így a lövegtorony nem fogja elérni az ellenséget. +rules.onlydepositcore.info = Megakadályozza, hogy az egységek nyersanyagokat helyezzenek el a támaszponton kívül más épületekbe. + content.item.name = Nyersanyagok content.liquid.name = Folyadékok content.unit.name = Egységek @@ -1823,13 +1828,13 @@ block.beam-tower.name = Sugártorony block.beam-link.name = Sugárhálózat block.turbine-condenser.name = Kondenzációs turbina block.chemical-combustion-chamber.name = Kémiai égetőkamra -block.pyrolysis-generator.name = Pirolíziserőmű +block.pyrolysis-generator.name = Pirolízis-erőmű block.vent-condenser.name = Vízleválasztó block.cliff-crusher.name = Sziklazúzó block.plasma-bore.name = Plazmafúró block.large-plasma-bore.name = Nagy plazmafúró block.impact-drill.name = Ütvefúró -block.eruption-drill.name = Kitörési fúró +block.eruption-drill.name = Kitöréses fúró block.core-bastion.name = Bástya block.core-citadel.name = Citadella block.core-acropolis.name = Akropolisz @@ -1898,7 +1903,7 @@ hint.derelict = Az [accent]elhagyatott[] szerkezetek régi bázisok maradványai hint.research = Használd a \ue875 [accent]Fejlesztési fa[] gombot, hogy új technológiákat fedezz fel. hint.research.mobile = Használd a \ue875 [accent]Fejlesztési fa[] gombot a \ue88c [accent]menüben[], hogy új technológiákat fedezz fel. hint.unitControl = Nyomd le a [accent][[bal Ctrl][] gombot, és kattints [accent]jobb egérgombbal[] a baráti egység vagy lövegtorony irányításához. -hint.unitControl.mobile = [accent][[Dupla koppintással][] irányíthatók kézileg a szövetséges egységek vagy lövegtornyok. +hint.unitControl.mobile = [accent][[Dupla koppintással][] a szövetséges egységek vagy lövegtornyok kézileg irányíthatóak. hint.unitSelectControl = Az egységek irányításához lépj be [accent]parancs módba[] a [accent]bal Shift[] lenyomva tartásával.\nParancs módban az egységek kijelöléséhez kattints, és húzd az egeret. A [accent]jobb egérgombbal[] küldd az egységeket a helyszínre vagy a célponthoz. hint.unitSelectControl.mobile = Az egységek irányításához lépj be [accent]parancs módba[] a bal alsó sarokban lévő [accent]parancs[] gombbal.\nParancs módban az egységek kiválasztásához érintsd meg a kijelzőt és húzással jelöld ki az egységeket. Koppintással küldd az egységeket a helyszínre vagy a célponthoz. hint.launch = Ha elegendő nyersanyagot gyűjtöttél össze, akkor [accent]lődd ki[] a támaszpontot a következő szektorba, úgy, hogy megnyitod a \ue827 [accent]Bolygótérképet[] a jobb alsó sarokban, és átforgatod az új helyszínre. @@ -1927,14 +1932,14 @@ gz.mine = Menj a földön lévő \uf8c4 [accent]rézérc[] közelébe, és katti gz.mine.mobile = Menj a földön lévő \uf8c4 [accent]rézérc[] közelébe, és koppints a bányászat megkezdéséhez. gz.research = Nyisd meg a \ue875 Fejlesztési fát.\nFejleszd ki a \uf870 [accent]Mechanikus fúrót[], majd válaszd ki a jobb alsó sarokban lévő menüből.\nKattints egy rézfoltra az elhelyezéséhez. gz.research.mobile = Nyisd meg a \ue875 Fejlesztési fát.\nFejleszd ki a \uf870 [accent]Mechanikus fúrót[], majd válaszd ki a jobb alsó sarokban lévő menüből.\nKattints egy rézfoltra az elhelyezéséhez.\n\nA megerősítéshez nyomd meg a jobb alsó sarokban lévő \ue800 [accent]pipát[]. -gz.conveyors = Fejleszd ki, és építs \uf896 [accent]szállítószalagokat[], hogy a kitermelt\nnyersanyagokat eljuttasd a fúróktól a támaszpontba.\n\nKattints és húzd az egeret, hogy több szállítószalagot helyezz el.\nHasználd a [accent]görgőt[] a forgatáshoz. -gz.conveyors.mobile = Fejleszd ki, és építs \uf896 [accent]szállítószalagokat[], hogy a kitermelt\nnyersanyagokat eljuttasd a fúróktól a támaszpontba.\n\nTartsd lenyomva az ujjad és húzd el, hogy több szállítószalagot helyezz el. +gz.conveyors = Fejleszd ki, és építs \uf896 [accent]Szállítószalagokat[], hogy a kitermelt\nnyersanyagokat eljuttasd a fúróktól a támaszpontba.\n\nKattints és húzd az egeret, hogy több szállítószalagot helyezz el.\nHasználd a [accent]görgőt[] a forgatáshoz. +gz.conveyors.mobile = Fejleszd ki, és építs \uf896 [accent]Szállítószalagokat[], hogy a kitermelt\nnyersanyagokat eljuttasd a fúróktól a támaszpontba.\n\nTartsd lenyomva az ujjad és húzd el, hogy több szállítószalagot helyezz el. gz.drills = Bővítsd a bányászati kapacitást.\nÉpíts több mechanikus fúrót.\nBányássz 100 rezet. gz.lead = Az \uf837 [accent]ólom[] egy másik gyakran használt nyersanyag.\nÉpíts fúrókat az ólom kitermelésére. gz.moveup = \ue804 Menj tovább a további utasításokért. -gz.turrets = Fejleszd ki, és építs 2 \uf861 [accent]Duo[] lövegtornyot, hogy megvédd a támaszpontot.\nA Duo lövegtornyoknak \uf838 [accent]lőszerre[] van szükségük, mely szállítószalaggal juttatható el hozzájuk. +gz.turrets = Fejleszd ki, és építs két \uf861 [accent]Duo[] lövegtornyot, hogy megvédd a támaszpontot.\nA Duo lövegtornyoknak \uf838 [accent]lőszerre[] van szükségük, mely szállítószalaggal juttatható el hozzájuk. gz.duoammo = Szállítószalagok segítségével lásd el [accent]rézzel[] a Duo lövegtornyokat. -gz.walls = A [accent]falak[] megakadályozhatják, hogy az épületekben károk keletkezzenek.\nÉpíts \uf8ae [accent]rézfalakat[] a lövegtornyok köré. +gz.walls = A [accent]falak[] megakadályozhatják, hogy az épületekben károk keletkezzenek.\nÉpíts \uf8ae [accent]Rézfalakat[] a lövegtornyok köré. gz.defend = Az ellenség közeledik, készülj fel a védekezésre. gz.aa = A repülő egységeket nem lehet könnyen elintézni a hagyományos lövegtornyokkal.\nA \uf860 [accent]Scatter[] lövegtornyok kiváló légelhárítást biztosítanak, de lőszerként \uf837 [accent]ólomra[] van szükségük. gz.scatterammo = Szállítószalagok segítségével lásd el \uf837 [accent]ólommal[] a Scatter lövegtornyokat. @@ -1946,17 +1951,17 @@ gz.finish = Építs több lövegtornyot, bányássz több nyersanyagot,\nés vé onset.mine = Kattints bal egérgombbal a \uf748 [accent]berillium[] kibányászáshoz a falakból.\n\nA mozgáshoz használd a [accent][[WASD] gombokat. onset.mine.mobile = Koppints a \uf748 [accent]berillium[] kibányászáshoz a falakból. -onset.research = Nyisd meg a \ue875 fejlesztési fát.\nFejleszd ki, és építs egy \uf73e [accent]kondenzációs turbinát[] a kürtőn.\nEz [accent]áramot[] fog termelni. -onset.bore = Fejleszd ki, és építs egy \uf741 [accent]plazmafúrót[].\nEz automatikusan bányássza ki a nyersanyagokat a falakból. -onset.power = Ahhoz, hogy [accent]árammal[] lásd el a plazmafúrót, fejleszd ki, és helyezz el egy \uf73d [accent]sugárcsomópontot[].\nSegítségükkel összekötheted a kondenzációs turbinát a plazmafúróval. -onset.ducts = Fejleszd ki, és építs \uf799 [accent]szállítószalagot[], hogy a kitermelt nyersanyagokat eljuttasd a plazmafúrótól a támaszpontba.\nKattints, és húzd az egeret több szállítószalag elhelyezéséhez.\nHasználd a [accent]görgőt[] a forgatáshoz. -onset.ducts.mobile = Fejleszd ki, és építs \uf799 [accent]szállítószalagot[], hogy a kitermelt nyersanyagokat eljuttasd a plazmafúrótól a támaszpontba.\n\nTartsd lenyomva az ujjad és húzd el, hogy több szállítószalagot helyezz el. +onset.research = Nyisd meg a \ue875 fejlesztési fát.\nFejleszd ki, és építs egy \uf73e [accent]Kondenzációs turbinát[] a kürtőn.\nEz [accent]áramot[] fog termelni. +onset.bore = Fejleszd ki, és építs egy \uf741 [accent]Plazmafúrót[].\nEz automatikusan bányássza ki a nyersanyagokat a falakból. +onset.power = Ahhoz, hogy [accent]árammal[] lásd el a plazmafúrót, fejleszd ki, és helyezz el egy \uf73d [accent]Sugárcsomópontot[].\nSegítségükkel összekötheted a kondenzációs turbinát a plazmafúróval. +onset.ducts = Fejleszd ki, és építs \uf799 [accent]Szállítószalagot[], hogy a kitermelt nyersanyagokat eljuttasd a plazmafúrótól a támaszpontba.\nKattints, és húzd az egeret több szállítószalag elhelyezéséhez.\nHasználd a [accent]görgőt[] a forgatáshoz. +onset.ducts.mobile = Fejleszd ki, és építs \uf799 [accent]Szállítószalagot[], hogy a kitermelt nyersanyagokat eljuttasd a plazmafúrótól a támaszpontba.\n\nTartsd lenyomva az ujjad és húzd el, hogy több szállítószalagot helyezz el. onset.moremine = Bővítsd a bányászati kapacitást.\nHelyezz el több plazmavágót, és a támogatásukhoz használj sugárcsomópontokat és szállítószalagokat.\nBányássz 200 berilliumot. onset.graphite = Az összetettebb épületekhez \uf835 [accent]grafit[] szükséges.\nÉpíts plazmavágókat a grafit kibányászásához. -onset.research2 = Kezdd el a [accent]gyárak[] fejlesztését.\nFejleszd ki a \uf74d [accent]sziklazúzót[] és a \uf779 [accent]szilícium ívkemencét[]. -onset.arcfurnace = A Szilícium ívkemencének \uf834 [accent]homokra[] és \uf835 [accent]grafitra[] van szüksége, hogy \uf82f [accent]szilíciumot[] gyártson.\nTovábbá [accent]áram[] is szükséges a működéséhez. -onset.crusher = Használj \uf74d [accent]sziklazúzókat[], hogy homokot bányász. -onset.fabricator = Használd az [accent]egységeket[], hogy felfedezd a pályát, megvédd az épületeket, és megtámadhasd velük az ellenséget. Fejleszd ki, és helyezz el egy \uf6a2 [accent]tankgyárat[]. +onset.research2 = Kezdd el a [accent]gyárak[] fejlesztését.\nFejleszd ki a \uf74d [accent]Sziklazúzót[] és a \uf779 [accent]Szilícium ívkemencét[]. +onset.arcfurnace = A szilícium ívkemencének \uf834 [accent]homokra[] és \uf835 [accent]grafitra[] van szüksége, hogy \uf82f [accent]szilíciumot[] gyártson.\nTovábbá [accent]áram[] is szükséges a működéséhez. +onset.crusher = Használj \uf74d [accent]Sziklazúzókat[], hogy homokot bányász. +onset.fabricator = Használd az [accent]egységeket[], hogy felfedezd a pályát, megvédd az épületeket, és megtámadhasd velük az ellenséget. Fejleszd ki, és helyezz el egy \uf6a2 [accent]Tankgyártót[]. onset.makeunit = Állíts elő egy egységet.\nHasználd a „?” gombot, hogy megnézd a kiválasztott gyár követelményeit. onset.turrets = Az egységek hatékonyak, de hatásosan alkalmazva a [accent]lövegtornyok[] jobb védelmi képességeket biztosítanak.\nHelyezz el egy \uf6eb [accent]Breach[] lövegtornyot.\nA lövegtornyoknak \uf748 [accent]lőszerre[] van szüksége. onset.turretammo = Szállítótalagok használatával lásd el a lövegtornyokat [accent]berillium[] lőszerrel. @@ -1966,18 +1971,18 @@ onset.defenses = [accent]Állíts fel védelmet:[lightgray] {0} onset.attack = Az ellenség most sebezhető. Indítsd ellentámadást. onset.cores = Új támaszpont csak a [accent]támaszpontcsempére[] helyezhető.\nAz új támaszpontok előretolt bázisként működnek, és megosztják a nyersanyagkészletüket más támaszpontokkal.\nHelyezz el egy \uf725 támaszpontot. onset.detect = Az ellenség 2 percen belül észrevesz téged.\nÁllíts fel védelmet, bányászatot és termelést. -onset.commandmode = Tartsd nyomva a [accent]Shift[] gombot, hogy [accent]parancs módba[] lépj.\n[accent]Bal egérgombbal és húzással[] lehet egységeket kijelölni.\n[accent]Jobb egérgombbal[] utasíthatók az egységek mozgásra vagy támadásra. -onset.commandmode.mobile = Nyomd meg a [accent]parancs gombot[], hogy [accent]parancs módba[] lépj.\nTartsd nyomva az ujjad, majd [accent]húzd[] az egységek kiválasztásához.\n[accent]Koppintással[] utasíthatók az egységek mozgásra vagy támadásra. -aegis.tungsten = Volfrámot [accent]ütvefúróval[] lehet bányászni.\nEnnek az épületnek [accent]vízre[] és [accent]áramra[] van szüksége. +onset.commandmode = Tartsd nyomva a [accent]Shift[] gombot, hogy [accent]parancs módba[] lépj.\n[accent]Bal egérgombbal és húzással[] lehet egységeket kijelölni.\n[accent]Jobb egérgombbal[] az egységek mozgásra vagy támadásra utasíthatóak. +onset.commandmode.mobile = Nyomd meg a [accent]parancs gombot[], hogy [accent]parancs módba[] lépj.\nTartsd nyomva az ujjad, majd [accent]húzd[] az egységek kiválasztásához.\n[accent]Koppintással[] az egységek mozgásra vagy támadásra utasíthatóak. +aegis.tungsten = Volfrámot [accent]Ütvefúróval[] lehet bányászni.\nEnnek az épületnek [accent]vízre[] és [accent]áramra[] van szüksége. -split.pickup = Egyes blokkok a támaszpont drónjával is felvehetők.\nVedd fel ezt a [accent]konténert[] és helyezd egy [accent]rakománycsomagolóba[].\n(A felvétel és lerakás alapértelmezett gombjai: [[ és ].) -split.pickup.mobile = Egyes blokkok a támaszpont drónjával is felvehetők.\nVedd fel ezt a [accent]konténert[] és helyezd egy [accent]rakománycsomagolóba[].\n(A felvételhez és lerakáshoz nyomd meg hosszan.) +split.pickup = Egyes blokkok a támaszpont drónjával is felvehetőek.\nVedd fel ezt a [accent]konténert[] és helyezd egy [accent]rakománycsomagolóba[].\n(A felvétel és lerakás alapértelmezett gombjai: [[ és ].) +split.pickup.mobile = Egyes blokkok a támaszpont drónjával is felvehetőek.\nVedd fel ezt a [accent]konténert[] és helyezd egy [accent]rakománycsomagolóba[].\n(A felvételhez és lerakáshoz nyomd meg hosszan.) split.acquire = Az egységek építéséhez volfrámot kell szerezned. -split.build = Az egységeket a fal másik oldalára kell eljuttatni.\nÉpíts két [accent]rakomány-tömegmozgatót[], egyet-egyet a fal mindkét oldalán.\nÁllítsd be a szállítási kapcsolatukat úgy, hogy kiválasztod az egyiket, majd kiválasztod a másikat. -split.container = A konténerekhez hasonlóan, az egységek is szállíthatók a [accent]rakomány-tömegmozgatóval[].\nÉpíts egy egységgyárat egy tömegmozgató mellé, hogy feltöltsd őket, majd küldd át őket a falon, hogy megtámadják az ellenséges bázist. +split.build = Az egységeket a fal másik oldalára kell eljuttatni.\nÉpíts két [accent]Rakomány-tömegmozgatót[], egyet-egyet a fal mindkét oldalán.\nÁllítsd be a szállítási kapcsolatukat úgy, hogy kiválasztod az egyiket, majd kiválasztod a másikat. +split.container = A konténerekhez hasonlóan, az egységek is szállíthatóak a [accent]Rakomány-tömegmozgatóval[].\nÉpíts egy egységgyárat egy tömegmozgató mellé, hogy feltöltsd őket, majd küldd át őket a falon, hogy megtámadják az ellenséges bázist. item.copper.description = Széleskörűen használatos építkezésnél és lőszerként. -item.copper.details = Réz. Szokatlanul bőséges fém a Serpulón. Megerősítés nélkül strukturálisan gyenge. +item.copper.details = Réz. Szokatlanul bőségesen elterjedt fém a Serpulón. Megerősítés nélkül strukturálisan gyenge. item.lead.description = Folyadékszállításnál és elektromos eszközökben használatos. item.lead.details = Sűrű. Közömbös. Széles körben használatos az akkumulátorokban.\nMegjegyzés: Valószínűleg mérgező a biológiai életformákra. Nem mintha sok maradt volna errefelé. item.metaglass.description = Folyadékszállító és -tárolóépületeknél használatos. @@ -2025,15 +2030,15 @@ block.message.description = Üzenetet tárol a szövetségesek kommunikációjá block.reinforced-message.description = Üzenetet tárol a szövetségesek közötti kommunikációhoz. block.world-message.description = A pályakészítésben használható üzenetblokk. Nem lehet megsemmisíteni. block.graphite-press.description = Grafittá préseli a szenet. -block.multi-press.description = Grafittá préseli a szenet. Hűtése vizet igényel. -block.silicon-smelter.description = A homokot és szenet szilíciummá finomítja. +block.multi-press.description = Grafittá sajtolja a szenet. Hűtése vizet igényel. +block.silicon-smelter.description = A homokot és a szenet szilíciummá finomítja. block.kiln.description = Ólomüveget olvaszt az ólomból és a homokból. block.plastanium-compressor.description = Olaj és titán felhasználásával műanyagot gyárt. block.phase-weaver.description = Tórium és homok keverékéből tóritkvarcot állít elő. block.surge-smelter.description = Titán, ólom, szilícium és réz ötvözésével elektrometált állít elő. block.cryofluid-mixer.description = Finom titánpor vízhez keverésével hűtőfolyadékot állít elő. block.blast-mixer.description = Robbanóelegyet készít a piratitból és a spórakapszulákból. -block.pyratite-mixer.description = Piratittá vegyíti a szenet, homokot és ólmot. +block.pyratite-mixer.description = Piratittá vegyíti a szenet, a homokot és az ólmot. block.melter.description = Salakká olvasztja a törmeléket. block.separator.description = Ásványi összetevőire bontja a salakot. block.spore-press.description = Olajat sajtol a spórakapszulából. @@ -2084,7 +2089,7 @@ block.mass-driver.description = Nagy hatótávolságú nyersanyagszállító esz block.mechanical-pump.description = Folyadékot szivattyúz és ad ki. Nem igényel áramot. block.rotary-pump.description = Folyadékot szivattyúz és ad ki. Áramot igényel. block.impulse-pump.description = Folyadékot szivattyúz és ad ki. -block.conduit.description = Folyadékot szállít. Pumpákkal és egyéb csővezetékekkel együtt használatos. +block.conduit.description = Folyadékot szállít. Szivattyúkkal és egyéb csővezetékekkel együtt használatos. block.pulse-conduit.description = Folyadékot szállít. Gyorsabban szállít, és nagyobb tárolókapacitású, mint a szokásos csővezeték. block.plated-conduit.description = Folyadékot szállít. Nem fogad el folyadékot oldalról. Nem szivárog, ha nincs a végén semmi. block.liquid-router.description = Egyenletesen háromfelé osztja szét a beérkező folyadékot. Bizonyos mennyiség tárolására is képes. @@ -2149,11 +2154,11 @@ block.tsunami.description = Erős folyadékhullámot lő az ellenségre. Eloltja block.silicon-crucible.description = Szilíciumot finomít homokból és szénből, piratitot használ kiegészítő hőforrásként. Forró környezetben még hatékonyabb. block.disassembler.description = Ritka ásványi összetevőket válogat ki a salakból, alacsony hatékonysággal. Képes tóriumot kiválogatni. block.overdrive-dome.description = Megnöveli a környező épületek termelési sebességét. A működtetése tóritkvarcot és szilíciumot igényel. -block.payload-conveyor.description = Nagy mennyiségű terhet mozgatni, például gyárakból érkező nyersanyagokat. Mágneses. Használható súlytalanságban. +block.payload-conveyor.description = Nagy méretű terhet mozgat, például gyárakból érkező egységeket. Mágneses. Használható súlytalanságban. block.payload-router.description = Háromfelé osztja szét a beérkező terhet. Rendezőként is szolgál, ha van megadva szűrő. Mágneses. Használható súlytalanságban. -block.ground-factory.description = Földi egységeket gyárt. A kész egységek azonnal hadra foghatók, vagy újratervezőkben továbbfejleszthetők. -block.air-factory.description = Légi egységeket gyárt. A kész egységek azonnal hadra foghatók, vagy újratervezőkben továbbfejleszthetők. -block.naval-factory.description = Vízi egységeket gyárt. A kész egységek azonnal hadra foghatók, vagy újratervezőkben továbbfejleszthetők. +block.ground-factory.description = Földi egységeket gyárt. Az elkészült egységek azonnal hadra foghatóak, vagy újratervezőkben továbbfejleszthetőek. +block.air-factory.description = Légi egységeket gyárt. Az elkészült egységek azonnal hadra foghatóak, vagy újratervezőkben továbbfejleszthetőek. +block.naval-factory.description = Vízi egységeket gyárt. Az elkészült egységek azonnal hadra foghatóak, vagy újratervezőkben továbbfejleszthetőek. block.additive-reconstructor.description = Kettes szintre fejleszti a beérkező egységeket. block.multiplicative-reconstructor.description = Hármas szintre fejleszti a beérkező egységeket. block.exponential-reconstructor.description = Négyes szintre fejleszti a beérkező egységeket. @@ -2183,7 +2188,7 @@ block.lustre.description = Lassan mozgó, egyszerre egy célpontra ható lézert block.scathe.description = Nagy erejű rakétát indít jelentős távolságokra lévő földi célpontok ellen. block.smite.description = Átütő erejű, villámló lövedékeket lő ki. block.malign.description = Lézertöltetekből álló célzott sortüzet zúdít az ellenséges célpontokra. Jelentős fűtést igényel. -block.silicon-arc-furnace.description = A homokot és grafitot szilíciummá finomítja. +block.silicon-arc-furnace.description = A homokot és a grafitot szilíciummá finomítja. block.oxidation-chamber.description = A berilliumot és az ózont oxiddá alakítja. Melléktermékként hőt bocsát ki. block.electric-heater.description = Fűti a vele szemben álló épületeket. Nagy mennyiségű áramot igényel. block.slag-heater.description = Fűti a vele szemben álló épületeket. Salakot igényel. @@ -2242,9 +2247,9 @@ block.build-tower.description = Automatikusan újjáépíti a hatósugarában l block.regen-projector.description = Lassan javítja a szövetséges építményeket egy négyzet alakú területen. Hidrogént igényel.\nTóritkvarc felhasználásával növelhető a hatékonysága. block.reinforced-container.description = Kis mennyiségű nyersanyagot tud tárolni. A tartalma kirakodók segítségével nyerhető ki. Nem növeli a támaszpont tárolókapacitását. block.reinforced-vault.description = Nagy mennyiségű nyersanyagot tud tárolni. A tartalma kirakodók segítségével nyerhető ki. Nem növeli a támaszpont tárolókapacitását. -block.tank-fabricator.description = Stell egységeket épít. A kimeneti egységek közvetlenül használhatók, vagy fejlesztésre újratervezőkbe küldhetők. -block.ship-fabricator.description = Elude egységeket épít. A kimeneti egységek közvetlenül használhatók, vagy fejlesztésre újratervezőkbe küldhetők. -block.mech-fabricator.description = Merui egységeket épít. A kimeneti egységek közvetlenül használhatók, vagy fejlesztésre újratervezőkbe küldhetők. +block.tank-fabricator.description = Stell egységeket épít. Az elkészült egységek azonnal hadra foghatóak, vagy újratervezőkben továbbfejleszthetőek. +block.ship-fabricator.description = Elude egységeket épít. Az elkészült egységek azonnal hadra foghatóak, vagy újratervezőkben továbbfejleszthetőek. +block.mech-fabricator.description = Merui egységeket épít. Az elkészült egységek azonnal hadra foghatóak, vagy újratervezőkben továbbfejleszthetőek. block.tank-assembler.description = Nagy méretű tankokat állít össze a beadott blokkokból és egységekből. A kimeneti szint modulok hozzáadásával növelhető. block.ship-assembler.description = Nagy méretű hajókat állít össze a beadott blokkokból és egységekből. A kimeneti szint modulok hozzáadásával növelhető. block.mech-assembler.description = Nagy méretű mecheket állít össze a beadott blokkokból és egységekből. A kimeneti szint modulok hozzáadásával növelhető. @@ -2452,7 +2457,7 @@ graphicstype.poly = Egy szabályos sokszög kitöltése. graphicstype.linepoly = Szabályos sokszög körvonalának rajzolása. graphicstype.triangle = Egy háromszög kitöltése. graphicstype.image = Kép rajzolása valamilyen tartalomról.\nPéldául: [accent]@router[] vagy [accent]@dagger[]. -graphicstype.print = Szöveget rajzol a kiírási pufferből.\nCsak ASCII karakterek használhatók.\nTörli a kiírás puffert. +graphicstype.print = Szöveget rajzol a kiírási pufferből.\nCsak ASCII karakterek használhatóak.\nTörli a kiírás puffert. lenum.always = Mindig igaz. lenum.idiv = Egész osztás. From 206adf46bf6a60c52aeeebc83fe1c80fc2c46c38 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Thu, 18 Apr 2024 18:36:00 +0000 Subject: [PATCH 121/348] Automatic bundle update --- core/assets/bundles/bundle_hu.properties | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index c839b306a1..092ccd0465 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -1378,8 +1378,6 @@ rules.weather = Időjárás rules.weather.frequency = Gyakoriság: rules.weather.always = Mindig rules.weather.duration = Időtartam: -rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. -rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. rules.placerangecheck.info = Megakadályozza, hogy a játékosok lövegtornyokat helyezzenek el az ellenséges épületek közelében. Amikor megpróbálnak egy lövegtornyot elhelyezni, az építési távolság megnő, így a lövegtorony nem fogja elérni az ellenséget. rules.onlydepositcore.info = Megakadályozza, hogy az egységek nyersanyagokat helyezzenek el a támaszponton kívül más épületekbe. From 1d30a9bcdbb7f5fa740690885a61903e2fd774bb Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 18 Apr 2024 17:07:33 -0400 Subject: [PATCH 122/348] comments --- core/src/mindustry/type/UnitType.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index 64e7c87785..7ad45fad64 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -1190,8 +1190,6 @@ public class UnitType extends UnlockableContent implements Senseable{ //region drawing - - /** Set up drawing state before calling! e.g. drawingPayload */ public void draw(Unit unit){ if(unit.inFogTo(Vars.player.team())) return; From 7b759d41df2954fcdb0316f3ca3fb4b3851d9784 Mon Sep 17 00:00:00 2001 From: BalaM314 <71201189+BalaM314@users.noreply.github.com> Date: Fri, 19 Apr 2024 19:10:47 +0530 Subject: [PATCH 123/348] Disable leg damage on disarmed units (#9755) * Disable leg damage on disarmed units * import * Disable crush damage from disarmed units --- core/src/mindustry/entities/comp/LegsComp.java | 3 ++- core/src/mindustry/entities/comp/TankComp.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/mindustry/entities/comp/LegsComp.java b/core/src/mindustry/entities/comp/LegsComp.java index 34b3cc3506..32ddc9b09f 100644 --- a/core/src/mindustry/entities/comp/LegsComp.java +++ b/core/src/mindustry/entities/comp/LegsComp.java @@ -24,6 +24,7 @@ abstract class LegsComp implements Posc, Rotc, Hitboxc, Flyingc, Unitc{ @Import float x, y, rotation, speedMultiplier; @Import UnitType type; @Import Team team; + @Import boolean disarmed; transient Leg[] legs = {}; transient float totalLength; @@ -191,7 +192,7 @@ abstract class LegsComp implements Posc, Rotc, Hitboxc, Flyingc, Unitc{ } } - if(type.legSplashDamage > 0){ + if(type.legSplashDamage > 0 && !disarmed){ Damage.damage(team, l.base.x, l.base.y, type.legSplashRange, type.legSplashDamage * state.rules.unitDamage(team), false, true); } } diff --git a/core/src/mindustry/entities/comp/TankComp.java b/core/src/mindustry/entities/comp/TankComp.java index 986a17cb56..afd4076ff5 100644 --- a/core/src/mindustry/entities/comp/TankComp.java +++ b/core/src/mindustry/entities/comp/TankComp.java @@ -18,7 +18,7 @@ import static mindustry.Vars.*; @Component abstract class TankComp implements Posc, Flyingc, Hitboxc, Unitc, ElevationMovec{ @Import float x, y, hitSize, rotation, speedMultiplier; - @Import boolean hovering; + @Import boolean hovering, disarmed; @Import UnitType type; @Import Team team; @@ -62,7 +62,7 @@ abstract class TankComp implements Posc, Flyingc, Hitboxc, Unitc, ElevationMovec } //TODO should this apply to the player team(s)? currently PvE due to balancing - if(type.crushDamage > 0 && (walked || deltaLen() >= 0.01f) && t != null && t.build != null && t.build.team != team + if(type.crushDamage > 0 && !disarmed && (walked || deltaLen() >= 0.01f) && t != null && t.build != null && t.build.team != team //damage radius is 1 tile smaller to prevent it from just touching walls as it passes && Math.max(Math.abs(dx), Math.abs(dy)) <= r - 1){ From a407d88e28bfb1faf5ad6dd04f812632c41f755c Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 19 Apr 2024 15:24:36 -0400 Subject: [PATCH 124/348] Hover pathfinding bugfixes --- core/src/mindustry/ai/ControlPathfinder.java | 9 +++++--- core/src/mindustry/ai/types/CommandAI.java | 22 ++++++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/core/src/mindustry/ai/ControlPathfinder.java b/core/src/mindustry/ai/ControlPathfinder.java index f50345a554..fe9f528384 100644 --- a/core/src/mindustry/ai/ControlPathfinder.java +++ b/core/src/mindustry/ai/ControlPathfinder.java @@ -1101,7 +1101,9 @@ public class ControlPathfinder implements Runnable{ //cache raycast results to run every time the world updates, and every tile the unit crosses if(lastRaycastTile != packedPos){ //near the destination, standard raycasting tends to break down, so use the more permissive 'near' variant that doesn't take into account edges of walls - raycastResult = unit.within(destination, tilesize * 2.5f) ? !raycastRect(unit.x, unit.y, destination.x, destination.y, team, cost, tileX, tileY, actualDestX, actualDestY, tileRectSize) : !raycast(team, cost, tileX, tileY, actualDestX, actualDestY); + raycastResult = unit.within(destination, tilesize * 2.5f) ? + !raycastRect(unit.x, unit.y, destination.x, destination.y, team, cost, tileX, tileY, actualDestX, actualDestY, tileRectSize) : + !raycast(team, cost, tileX, tileY, actualDestX, actualDestY); if(request != null){ request.lastRaycastTile = packedPos; @@ -1141,7 +1143,7 @@ public class ControlPathfinder implements Runnable{ boolean recalc = false; //TODO last pos can change if the flowfield changes. - if(initialTileOn.pos() != request.lastTile || request.lastTargetTile == null){ + if(initialTileOn.pos() != request.lastTile || request.lastTargetTile == null || true){ boolean anyNearSolid = false; //find the next tile until one near a solid block is discovered @@ -1176,7 +1178,7 @@ public class ControlPathfinder implements Runnable{ if(!(current == null || (costId == costIdGround && current.dangerous() && !tileOn.dangerous()))){ //when anyNearSolid is false, no solid tiles have been encountered anywhere so far, so raycasting is a waste of time - if(anyNearSolid && !tileOn.dangerous() && raycastRect(unit.x, unit.y, current.x * tilesize, current.y * tilesize, team, cost, initialTileOn.x, initialTileOn.y, current.x, current.y, tileRectSize)){ + if(anyNearSolid && !(tileOn.dangerous() && costId == costIdGround) && raycastRect(unit.x, unit.y, current.x * tilesize, current.y * tilesize, team, cost, initialTileOn.x, initialTileOn.y, current.x, current.y, tileRectSize)){ //TODO this may be a mistake if(tileOn == initialTileOn){ @@ -1206,6 +1208,7 @@ public class ControlPathfinder implements Runnable{ } if(request.lastTargetTile != null){ + Fx.breakBlock.at(request.lastTargetTile.worldx(), request.lastTargetTile.worldy(), 1); out.set(request.lastTargetTile); request.lastTile = recalc ? -1 : initialTileOn.pos(); return true; diff --git a/core/src/mindustry/ai/types/CommandAI.java b/core/src/mindustry/ai/types/CommandAI.java index dec38c14ed..763916539a 100644 --- a/core/src/mindustry/ai/types/CommandAI.java +++ b/core/src/mindustry/ai/types/CommandAI.java @@ -205,6 +205,9 @@ public class CommandAI extends AIController{ boolean alwaysArrive = false; + float engageRange = unit.type.range - 10f; + boolean withinAttackRange = attackTarget != null && unit.within(attackTarget, engageRange) && stance != UnitStance.ram; + if(targetPos != null){ boolean move = true, isFinalPoint = commandQueue.size == 0; vecOut.set(targetPos); @@ -249,7 +252,16 @@ public class CommandAI extends AIController{ timeSpentBlocked = 0f; } - move = controlPath.getPathPosition(unit, vecMovePos, targetPos, vecOut, noFound) && (!blockingUnit || timeSpentBlocked > maxBlockTime); + //if the unit is next to the target, stop asking the pathfinder how to get there, it's a waste of CPU + //TODO maybe stop moving too? + if(withinAttackRange){ + move = true; + noFound[0] = false; + vecOut.set(vecMovePos); + }else{ + move = controlPath.getPathPosition(unit, vecMovePos, targetPos, vecOut, noFound) && (!blockingUnit || timeSpentBlocked > maxBlockTime); + } + //rare case where unit must be perfectly aligned (happens with 1-tile gaps) alwaysArrive = vecOut.epsilonEquals(unit.tileX() * tilesize, unit.tileY() * tilesize); //we've reached the final point if the returned coordinate is equal to the supplied input @@ -268,18 +280,16 @@ public class CommandAI extends AIController{ vecOut.set(vecMovePos); } - float engageRange = unit.type.range - 10f; - if(move){ if(unit.type.circleTarget && attackTarget != null){ target = attackTarget; circleAttack(80f); }else{ moveTo(vecOut, - attackTarget != null && unit.within(attackTarget, engageRange) && stance != UnitStance.ram ? engageRange : + withinAttackRange ? engageRange : unit.isGrounded() ? 0f : - attackTarget != null && stance != UnitStance.ram ? engageRange : - 0f, unit.isFlying() ? 40f : 100f, false, null, isFinalPoint || alwaysArrive); + attackTarget != null && stance != UnitStance.ram ? engageRange : 0f, + unit.isFlying() ? 40f : 100f, false, null, isFinalPoint || alwaysArrive); } } From a074c9f3f5a357d7521a549bad39b285e52270fe Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 19 Apr 2024 15:25:10 -0400 Subject: [PATCH 125/348] debug removed --- core/src/mindustry/ai/ControlPathfinder.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/ai/ControlPathfinder.java b/core/src/mindustry/ai/ControlPathfinder.java index fe9f528384..0e1734969c 100644 --- a/core/src/mindustry/ai/ControlPathfinder.java +++ b/core/src/mindustry/ai/ControlPathfinder.java @@ -1208,7 +1208,9 @@ public class ControlPathfinder implements Runnable{ } if(request.lastTargetTile != null){ - Fx.breakBlock.at(request.lastTargetTile.worldx(), request.lastTargetTile.worldy(), 1); + if(showDebug){ + Fx.breakBlock.at(request.lastTargetTile.worldx(), request.lastTargetTile.worldy(), 1); + } out.set(request.lastTargetTile); request.lastTile = recalc ? -1 : initialTileOn.pos(); return true; From 3595c350ab1d644332908afd866ea91faf52b147 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 20 Apr 2024 10:25:43 -0400 Subject: [PATCH 126/348] Respect particle effect aspect ratio --- core/src/mindustry/entities/effect/ParticleEffect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/entities/effect/ParticleEffect.java b/core/src/mindustry/entities/effect/ParticleEffect.java index 0913cecb62..4da451b08f 100644 --- a/core/src/mindustry/entities/effect/ParticleEffect.java +++ b/core/src/mindustry/entities/effect/ParticleEffect.java @@ -88,7 +88,7 @@ public class ParticleEffect extends Effect{ rv.trns(realRotation + rand.range(cone), !randLength ? l : rand.random(l)); float x = rv.x, y = rv.y; - Draw.rect(tex, ox + x, oy + y, rad, rad, realRotation + offset + e.time * spin); + Draw.rect(tex, ox + x, oy + y, rad, rad / tex.ratio(), realRotation + offset + e.time * spin); Drawf.light(ox + x, oy + y, rad * lightScl, lightColor, lightOpacity * Draw.getColor().a); } } From 3a4f3e7fe7867c983bc35203ec2dbb43b66583d9 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 20 Apr 2024 11:18:57 -0400 Subject: [PATCH 127/348] Don't refund deconstruction in sandbox --- core/src/mindustry/world/blocks/ConstructBlock.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/world/blocks/ConstructBlock.java b/core/src/mindustry/world/blocks/ConstructBlock.java index 796c7369bd..f45822964a 100644 --- a/core/src/mindustry/world/blocks/ConstructBlock.java +++ b/core/src/mindustry/world/blocks/ConstructBlock.java @@ -353,7 +353,7 @@ public class ConstructBlock extends Block{ if(progress <= current.deconstructThreshold || state.rules.infiniteResources){ //add any leftover items that weren't obtained due to rounding errors - if(core != null){ + if(core != null && !state.rules.infiniteResources){ for(int i = 0; i < itemsLeft.length; i++){ int target = Mathf.round(requirements[i].amount * state.rules.buildCostMultiplier * state.rules.deconstructRefundMultiplier); int remaining = target - itemsLeft[i]; From 169a186d63300ef3e94c544162edf90eb6167bc8 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 20 Apr 2024 11:36:08 -0400 Subject: [PATCH 128/348] Coolant display fix --- .../blocks/defense/turrets/ReloadTurret.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/world/blocks/defense/turrets/ReloadTurret.java b/core/src/mindustry/world/blocks/defense/turrets/ReloadTurret.java index 5bf13c7c91..8b683c0c29 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/ReloadTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/ReloadTurret.java @@ -1,6 +1,9 @@ package mindustry.world.blocks.defense.turrets; import arc.math.*; +import arc.struct.*; +import arc.util.*; +import mindustry.type.*; import mindustry.world.consumers.*; import mindustry.world.meta.*; @@ -19,7 +22,23 @@ public class ReloadTurret extends BaseTurret{ if(coolant != null){ stats.remove(Stat.booster); - stats.add(Stat.booster, StatValues.boosters(reload, coolant.amount, coolantMultiplier, true, l -> l.coolant && consumesLiquid(l))); + + //TODO this is very hacky, there is no current way to check if a ConsumeLiquidBase accepts something individually. fix later + ObjectSet notBooster = content.liquids().select(l -> { + for(Consume c : consumers){ + if(!c.booster && c != coolant && + ((c instanceof ConsumeLiquid cl && cl.liquid == l) || + (c instanceof ConsumeLiquids cl2 && Structs.contains(cl2.liquids, s -> s.liquid == l)) || + (c instanceof ConsumeLiquidFilter clf && clf.filter.get(l)))){ + + Log.infoList(this, c, l); + return true; + } + } + return false; + }).asSet(); + + stats.add(Stat.booster, StatValues.boosters(reload, coolant.amount, coolantMultiplier, true, l -> l.coolant && consumesLiquid(l) && !notBooster.contains(l))); } } From 066e18ae4096f77adbdb1134a86366558261a43d Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 20 Apr 2024 11:36:31 -0400 Subject: [PATCH 129/348] Coolant display fix --- .../src/mindustry/world/blocks/defense/turrets/ReloadTurret.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/mindustry/world/blocks/defense/turrets/ReloadTurret.java b/core/src/mindustry/world/blocks/defense/turrets/ReloadTurret.java index 8b683c0c29..9203869459 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/ReloadTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/ReloadTurret.java @@ -31,7 +31,6 @@ public class ReloadTurret extends BaseTurret{ (c instanceof ConsumeLiquids cl2 && Structs.contains(cl2.liquids, s -> s.liquid == l)) || (c instanceof ConsumeLiquidFilter clf && clf.filter.get(l)))){ - Log.infoList(this, c, l); return true; } } From 9859efb97f77c7ba2205fc3cdf9c0f4643e63fdd Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 20 Apr 2024 18:59:40 -0400 Subject: [PATCH 130/348] Pathfinder bugfixes --- core/src/mindustry/ai/ControlPathfinder.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/src/mindustry/ai/ControlPathfinder.java b/core/src/mindustry/ai/ControlPathfinder.java index 0e1734969c..a63c6115e0 100644 --- a/core/src/mindustry/ai/ControlPathfinder.java +++ b/core/src/mindustry/ai/ControlPathfinder.java @@ -1143,7 +1143,7 @@ public class ControlPathfinder implements Runnable{ boolean recalc = false; //TODO last pos can change if the flowfield changes. - if(initialTileOn.pos() != request.lastTile || request.lastTargetTile == null || true){ + if(initialTileOn.pos() != request.lastTile || request.lastTargetTile == null){ boolean anyNearSolid = false; //find the next tile until one near a solid block is discovered @@ -1361,8 +1361,7 @@ public class ControlPathfinder implements Runnable{ private static boolean passable(int team, PathCost cost, int pos){ int amount = cost.getCost(team, pathfinder.tiles[pos]); - //edge case: naval reports costs of 6000+ for non-liquids, even though they are not technically passable - return amount != impassable && !(cost == costNaval && amount >= 6000); + return amount < 50; } private static boolean solid(int team, PathCost type, int x, int y){ @@ -1371,7 +1370,7 @@ public class ControlPathfinder implements Runnable{ private static boolean solid(int team, PathCost type, int tilePos, boolean checkWall){ int cost = cost(team, type, tilePos); - return cost == impassable || (checkWall && cost >= 6000); + return cost >= 50; } private static int cost(int team, PathCost cost, int tilePos){ From d8c39089c67e8ba0b743aecd045613ad36819ff3 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 20 Apr 2024 19:11:02 -0400 Subject: [PATCH 131/348] Fixed #9760 --- core/src/mindustry/ai/ControlPathfinder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/ai/ControlPathfinder.java b/core/src/mindustry/ai/ControlPathfinder.java index a63c6115e0..624e87124e 100644 --- a/core/src/mindustry/ai/ControlPathfinder.java +++ b/core/src/mindustry/ai/ControlPathfinder.java @@ -1361,7 +1361,7 @@ public class ControlPathfinder implements Runnable{ private static boolean passable(int team, PathCost cost, int pos){ int amount = cost.getCost(team, pathfinder.tiles[pos]); - return amount < 50; + return amount != impassable && amount < 50; } private static boolean solid(int team, PathCost type, int x, int y){ @@ -1370,7 +1370,7 @@ public class ControlPathfinder implements Runnable{ private static boolean solid(int team, PathCost type, int tilePos, boolean checkWall){ int cost = cost(team, type, tilePos); - return cost >= 50; + return cost == impassable || cost >= 50; } private static int cost(int team, PathCost cost, int tilePos){ From 0cdbeef9800a232bd626cc6c4ec317b04cb9f3b7 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 20 Apr 2024 19:13:12 -0400 Subject: [PATCH 132/348] Fixed #9759 --- core/src/mindustry/service/GameService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/service/GameService.java b/core/src/mindustry/service/GameService.java index 7b31d10711..ce008a3c88 100644 --- a/core/src/mindustry/service/GameService.java +++ b/core/src/mindustry/service/GameService.java @@ -291,7 +291,7 @@ public class GameService{ }); Events.on(SectorLaunchLoadoutEvent.class, e -> { - if(!schematics.isDefaultLoadout(e.loadout)){ + if(e.sector.planet == Planets.serpulo && !schematics.isDefaultLoadout(e.loadout)){ launchCoreSchematic.complete(); } }); From bc2f344c527f8d3f78b8d05fed28654c166282f5 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 21 Apr 2024 09:53:54 -0400 Subject: [PATCH 133/348] Fixed #9765 --- core/src/mindustry/entities/Damage.java | 2 +- core/src/mindustry/world/blocks/payloads/PayloadLoader.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/entities/Damage.java b/core/src/mindustry/entities/Damage.java index 158cba605b..a1e7263ad0 100644 --- a/core/src/mindustry/entities/Damage.java +++ b/core/src/mindustry/entities/Damage.java @@ -25,7 +25,6 @@ public class Damage{ private static final Rect rect = new Rect(); private static final Rect hitrect = new Rect(); private static final Vec2 vec = new Vec2(), seg1 = new Vec2(), seg2 = new Vec2(); - private static final Seq units = new Seq<>(); private static final IntSet collidedBlocks = new IntSet(); private static final IntFloatMap damages = new IntFloatMap(); private static final Seq collided = new Seq<>(); @@ -41,6 +40,7 @@ public class Damage{ public static void applySuppression(Team team, float x, float y, float range, float reload, float maxDelay, float applyParticleChance, @Nullable Position source){ applySuppression(team, x, y, range, reload, maxDelay, applyParticleChance, source, Pal.sapBullet); } + public static void applySuppression(Team team, float x, float y, float range, float reload, float maxDelay, float applyParticleChance, @Nullable Position source, Color effectColor){ builds.clear(); indexer.eachBlock(null, x, y, range, build -> build.team != team, build -> { diff --git a/core/src/mindustry/world/blocks/payloads/PayloadLoader.java b/core/src/mindustry/world/blocks/payloads/PayloadLoader.java index 67a01ba0a7..9d9328597e 100644 --- a/core/src/mindustry/world/blocks/payloads/PayloadLoader.java +++ b/core/src/mindustry/world/blocks/payloads/PayloadLoader.java @@ -153,8 +153,9 @@ public class PayloadLoader extends PayloadBlock{ //load up items if(payload.block().hasItems && items.any()){ - boolean acceptedAny = false; + boolean acceptedAny = true; if(efficiency > 0.01f && timer(timerLoad, loadTime / efficiency)){ + acceptedAny = false; //load up items a set amount of times for(int j = 0; j < itemsLoaded && items.any(); j++){ From 4c4476ae95c94e5d21d9dc3fe0e6f69a4f73e4f7 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 21 Apr 2024 10:05:05 -0400 Subject: [PATCH 134/348] Fixed #9763 --- core/src/mindustry/graphics/OverlayRenderer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/graphics/OverlayRenderer.java b/core/src/mindustry/graphics/OverlayRenderer.java index 844a087c8e..b9dc661054 100644 --- a/core/src/mindustry/graphics/OverlayRenderer.java +++ b/core/src/mindustry/graphics/OverlayRenderer.java @@ -258,8 +258,9 @@ public class OverlayRenderer{ } public void checkApplySelection(Unit u){ - if(unitFade > 0 && lastSelect == u){ - Draw.mixcol(Pal.accent, unitFade); + if(unitFade > 0.001f && lastSelect == u){ + Color prev = Draw.getMixColor(); + Draw.mixcol(prev.a > 0.001f ? prev.lerp(Pal.accent, unitFade) : Pal.accent, Math.max(unitFade, prev.a)); } } From caf9f2a6e3c3a0888910f50d5c49635f988c7512 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 21 Apr 2024 10:07:14 -0400 Subject: [PATCH 135/348] Fixed #9761 --- core/src/mindustry/world/blocks/payloads/UnitPayload.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/mindustry/world/blocks/payloads/UnitPayload.java b/core/src/mindustry/world/blocks/payloads/UnitPayload.java index fb22ceb94c..d0b0b86a0a 100644 --- a/core/src/mindustry/world/blocks/payloads/UnitPayload.java +++ b/core/src/mindustry/world/blocks/payloads/UnitPayload.java @@ -14,6 +14,7 @@ import mindustry.entities.EntityCollisions.*; import mindustry.entities.*; import mindustry.game.EventType.*; import mindustry.gen.*; +import mindustry.graphics.*; import mindustry.type.*; import static mindustry.Vars.*; @@ -153,6 +154,8 @@ public class UnitPayload implements Payload{ //draw warning if(overlayTime > 0){ + float z = Draw.z(); + Draw.z(Layer.groundUnit + 1f); var region = overlayRegion == null ? Icon.warning.getRegion() : overlayRegion; Draw.color(Color.scarlet); Draw.alpha(0.8f * Interp.exp5Out.apply(overlayTime)); @@ -163,6 +166,7 @@ public class UnitPayload implements Payload{ Draw.reset(); overlayTime = Math.max(overlayTime - Time.delta/overlayDuration, 0f); + Draw.z(z); } } From 022b3951d2c888bccd446e68248ac98152411ef3 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 21 Apr 2024 10:16:07 -0400 Subject: [PATCH 136/348] Fixed #9762 --- core/src/mindustry/ai/ControlPathfinder.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/core/src/mindustry/ai/ControlPathfinder.java b/core/src/mindustry/ai/ControlPathfinder.java index 624e87124e..cdc35db17f 100644 --- a/core/src/mindustry/ai/ControlPathfinder.java +++ b/core/src/mindustry/ai/ControlPathfinder.java @@ -23,6 +23,7 @@ import static mindustry.ai.Pathfinder.*; //https://www.gameaipro.com/GameAIPro/GameAIPro_Chapter23_Crowd_Pathfinding_and_Steering_Using_Flow_Field_Tiles.pdf public class ControlPathfinder implements Runnable{ private static final int wallImpassableCap = 1_000_000; + private static final int solidCap = 7000; public static boolean showDebug; @@ -1315,7 +1316,7 @@ public class ControlPathfinder implements Runnable{ private static boolean overlap(int team, PathCost type, int x, int y, float startX, float startY, float endX, float endY, float rectSize){ if(x < 0 || y < 0 || x >= wwidth || y >= wheight) return false; - if(!passable(team, type, x + y * wwidth)){ + if(!nearPassable(team, type, x + y * wwidth)){ return Intersector.intersectSegmentRectangleFast(startX, startY, endX, endY, x * tilesize - rectSize/2f, y * tilesize - rectSize/2f, rectSize, rectSize); } return false; @@ -1329,7 +1330,7 @@ public class ControlPathfinder implements Runnable{ while(x >= 0 && y >= 0 && x < ww && y < wh){ if( - !passable(team, type, x + y * wwidth) || + !nearPassable(team, type, x + y * wwidth) || overlap(team, type, x + 1, y, startX, startY, endX, endY, rectSize) || overlap(team, type, x - 1, y, startX, startY, endX, endY, rectSize) || overlap(team, type, x, y + 1, startX, startY, endX, endY, rectSize) || @@ -1360,6 +1361,11 @@ public class ControlPathfinder implements Runnable{ } private static boolean passable(int team, PathCost cost, int pos){ + int amount = cost.getCost(team, pathfinder.tiles[pos]); + return amount != impassable && amount < solidCap; + } + + private static boolean nearPassable(int team, PathCost cost, int pos){ int amount = cost.getCost(team, pathfinder.tiles[pos]); return amount != impassable && amount < 50; } @@ -1370,7 +1376,7 @@ public class ControlPathfinder implements Runnable{ private static boolean solid(int team, PathCost type, int tilePos, boolean checkWall){ int cost = cost(team, type, tilePos); - return cost == impassable || cost >= 50; + return cost == impassable || cost >= solidCap; } private static int cost(int team, PathCost cost, int tilePos){ From a7a9a99780c4f7ad61f1f1777952344f1c6042c7 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 21 Apr 2024 10:19:39 -0400 Subject: [PATCH 137/348] More pathfinder tweaks --- core/src/mindustry/ai/ControlPathfinder.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/ai/ControlPathfinder.java b/core/src/mindustry/ai/ControlPathfinder.java index cdc35db17f..01cfb849df 100644 --- a/core/src/mindustry/ai/ControlPathfinder.java +++ b/core/src/mindustry/ai/ControlPathfinder.java @@ -1367,7 +1367,9 @@ public class ControlPathfinder implements Runnable{ private static boolean nearPassable(int team, PathCost cost, int pos){ int amount = cost.getCost(team, pathfinder.tiles[pos]); - return amount != impassable && amount < 50; + //for standard units: never consider deep water (cost = 6000) passable + //for leg units: consider it passable + return amount != impassable && amount < (cost == costLegs ? solidCap : 50); } private static boolean solid(int team, PathCost type, int x, int y){ From 430114d9312efdc66c3d9c45ad34d8e450158864 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 21 Apr 2024 10:46:50 -0400 Subject: [PATCH 138/348] Possible building raycast damage fix --- core/src/mindustry/entities/Damage.java | 26 +++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/core/src/mindustry/entities/Damage.java b/core/src/mindustry/entities/Damage.java index a1e7263ad0..780edb5b3b 100644 --- a/core/src/mindustry/entities/Damage.java +++ b/core/src/mindustry/entities/Damage.java @@ -175,21 +175,23 @@ public class Damage{ distances.clear(); - World.raycast(b.tileX(), b.tileY(), World.toTile(b.x + vec.x), World.toTile(b.y + vec.y), (x, y) -> { - //add distance to list so it can be processed - var build = world.build(x, y); + if(b.type.collidesGround && b.type.collidesTiles){ + World.raycast(b.tileX(), b.tileY(), World.toTile(b.x + vec.x), World.toTile(b.y + vec.y), (x, y) -> { + //add distance to list so it can be processed + var build = world.build(x, y); - if(build != null && build.team != b.team && build.collide(b) && b.checkUnderBuild(build, x * tilesize, y * tilesize)){ - distances.add(b.dst(build)); + if(build != null && build.team != b.team && build.collide(b) && b.checkUnderBuild(build, x * tilesize, y * tilesize)){ + distances.add(b.dst(build)); - if(laser && build.absorbLasers()){ - maxDst = Math.min(maxDst, b.dst(build)); - return true; + if(laser && build.absorbLasers()){ + maxDst = Math.min(maxDst, b.dst(build)); + return true; + } } - } - return false; - }); + return false; + }); + } Units.nearbyEnemies(b.team, rect, u -> { u.hitbox(hitrect); @@ -247,7 +249,7 @@ public class Damage{ collidedBlocks.clear(); vec.trnsExact(angle, length); - if(hitter.type.collidesGround){ + if(hitter.type.collidesGround && hitter.type.collidesTiles){ seg1.set(x, y); seg2.set(seg1).add(vec); World.raycastEachWorld(x, y, seg2.x, seg2.y, (cx, cy) -> { From a62540ec7e4989f0d9ed2a5331b3f70b4b5cab43 Mon Sep 17 00:00:00 2001 From: Ignacio Date: Sun, 21 Apr 2024 17:02:04 +0200 Subject: [PATCH 139/348] Update bundle_es.properties (#9756) There was an ortographic mistake in the Spanish translation. Changed 2033 & 2034 "recive" for "recibe". --- core/assets/bundles/bundle_es.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index ce0c335822..9200ef1e0a 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -2030,8 +2030,8 @@ block.separator.description = Separa el magma en sus componentes minerales. block.spore-press.description = Comprime vainas de esporas en petróleo. block.pulverizer.description = Prensa chatarra hasta obtener arena. block.coal-centrifuge.description = Solidifica petróleo en trozos de carbón. -block.incinerator.description = Vaporiza cualquier líquido o material que recive. -block.power-void.description = Elimina toda la energía que recive. Solo disponible en el modo Libre. +block.incinerator.description = Vaporiza cualquier líquido o material que recibe. +block.power-void.description = Elimina toda la energía que recibe. Solo disponible en el modo Libre. block.power-source.description = Genera energía infinita. Solo disponible en el modo Libre. block.item-source.description = Genera objetos de forma infinita. Solo disponible en el modo Libre. block.item-void.description = Destruye los objetos que entran en él. Solo disponible en el modo Libre. From 14a155147de4bd6af1c54faec47d3bd5b7df84ad Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 21 Apr 2024 19:16:52 -0400 Subject: [PATCH 140/348] Item turret status fix --- .../mindustry/world/blocks/defense/turrets/ItemTurret.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java b/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java index 5e37d32a97..1a709dc5a8 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java @@ -79,8 +79,8 @@ public class ItemTurret extends Turret{ @Override public float efficiency(Building build){ - //valid when there's any ammo in the turret - return build instanceof ItemTurretBuild it && !it.ammo.isEmpty() ? 1f : 0f; + //valid when it can shoot + return build instanceof ItemTurretBuild it && it.hasAmmo() ? 1f : 0f; } @Override From d7191e4f715823fa66286f1bed021deb849f27d3 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 22 Apr 2024 08:43:25 -0400 Subject: [PATCH 141/348] Fixed #9769 --- core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java b/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java index 1a709dc5a8..14aa007d35 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java @@ -80,7 +80,7 @@ public class ItemTurret extends Turret{ @Override public float efficiency(Building build){ //valid when it can shoot - return build instanceof ItemTurretBuild it && it.hasAmmo() ? 1f : 0f; + return build instanceof ItemTurretBuild it && it.ammo.size > 0 && (it.ammo.peek().amount >= ammoPerShot || it.cheating()) ? 1f : 0f; } @Override From 49489ffadc4a3d6c19c83124768c5ce9e752921a Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 22 Apr 2024 09:20:24 -0400 Subject: [PATCH 142/348] Don't rotate base shadow --- core/src/mindustry/world/Block.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/world/Block.java b/core/src/mindustry/world/Block.java index 2e9f291b7d..613c99cfc3 100644 --- a/core/src/mindustry/world/Block.java +++ b/core/src/mindustry/world/Block.java @@ -409,7 +409,7 @@ public class Block extends UnlockableContent implements Senseable{ Draw.rect( variants == 0 ? customShadowRegion : variantShadowRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantShadowRegions.length - 1))], - tile.drawx(), tile.drawy(), tile.build == null ? 0f : tile.build.drawrot()); + tile.drawx(), tile.drawy()); Draw.color(); } From 31f4957657ba2922d9d54b085ece59d5e96ce4f5 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 22 Apr 2024 17:31:13 -0400 Subject: [PATCH 143/348] Removed UnitComp#uiIcon() --- core/src/mindustry/entities/comp/BlockUnitComp.java | 6 ------ core/src/mindustry/entities/comp/PlayerComp.java | 2 +- core/src/mindustry/entities/comp/UnitComp.java | 7 +------ core/src/mindustry/graphics/MinimapRenderer.java | 2 +- 4 files changed, 3 insertions(+), 14 deletions(-) diff --git a/core/src/mindustry/entities/comp/BlockUnitComp.java b/core/src/mindustry/entities/comp/BlockUnitComp.java index bd13c88991..5ab3327f76 100644 --- a/core/src/mindustry/entities/comp/BlockUnitComp.java +++ b/core/src/mindustry/entities/comp/BlockUnitComp.java @@ -40,12 +40,6 @@ abstract class BlockUnitComp implements Unitc{ @Replace @Override public TextureRegion icon(){ - return tile.block.fullIcon; - } - - @Replace - @Override - public TextureRegion uiIcon(){ return tile.block.uiIcon; } diff --git a/core/src/mindustry/entities/comp/PlayerComp.java b/core/src/mindustry/entities/comp/PlayerComp.java index 45a80b7c1f..50ce197306 100644 --- a/core/src/mindustry/entities/comp/PlayerComp.java +++ b/core/src/mindustry/entities/comp/PlayerComp.java @@ -74,7 +74,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra //display default icon for dead players if(dead()) return core() == null ? UnitTypes.alpha.uiIcon : ((CoreBlock)bestCore().block).unitType.uiIcon; - return unit.uiIcon(); + return unit.icon(); } public boolean displayAmmo(){ diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index eb9b64f032..1b4e095909 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -666,13 +666,8 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I } } - /** @return a preview icon for this unit. */ - public TextureRegion icon(){ - return type.fullIcon; - } - /** @return a preview UI icon for this unit. */ - public TextureRegion uiIcon(){ + public TextureRegion icon(){ return type.uiIcon; } diff --git a/core/src/mindustry/graphics/MinimapRenderer.java b/core/src/mindustry/graphics/MinimapRenderer.java index a14935a379..e5f42b2c9a 100644 --- a/core/src/mindustry/graphics/MinimapRenderer.java +++ b/core/src/mindustry/graphics/MinimapRenderer.java @@ -166,7 +166,7 @@ public class MinimapRenderer{ if(unit.inFogTo(player.team()) || !unit.type.drawMinimap) continue; float scale = Scl.scl(1f) * tilesize * 3; - var region = unit.uiIcon(); + var region = unit.icon(); Draw.mixcol(unit.team.color, 1f); Draw.rect(region, unit.x, unit.y, scale, scale * (float)region.height / region.width, unit.rotation() - 90); From 3a91a8eb0051db25f538619e9686eebfb7daa8b7 Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 23 Apr 2024 09:17:38 -0400 Subject: [PATCH 144/348] Fixed #9771 --- core/src/mindustry/content/Blocks.java | 2 +- core/src/mindustry/ui/dialogs/KeybindDialog.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 36dfb1ee3d..d0f1cabe59 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -4108,7 +4108,7 @@ public class Blocks{ rotateSpeed = 3f; coolant = consume(new ConsumeLiquid(Liquids.water, 15f / 60f)); - limitRange(16f); + limitRange(25f); }}; sublimate = new ContinuousLiquidTurret("sublimate"){{ diff --git a/core/src/mindustry/ui/dialogs/KeybindDialog.java b/core/src/mindustry/ui/dialogs/KeybindDialog.java index 1f9ead9a31..58c186ff31 100644 --- a/core/src/mindustry/ui/dialogs/KeybindDialog.java +++ b/core/src/mindustry/ui/dialogs/KeybindDialog.java @@ -213,7 +213,6 @@ public class KeybindDialog extends Dialog{ @Override public boolean keyDown(InputEvent event, KeyCode keycode){ rebindDialog.hide(); - if(keycode == KeyCode.escape) return false; rebind(section, name, keycode); return false; } From 60a829469414ce75e3a68976ecc09f14ab00a54c Mon Sep 17 00:00:00 2001 From: HubsvVN <157294903+HubsvVN@users.noreply.github.com> Date: Tue, 23 Apr 2024 20:19:40 +0700 Subject: [PATCH 145/348] Update servers_v7.json (#9772) --- servers_v7.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/servers_v7.json b/servers_v7.json index b44f5d74f4..84a60822e0 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -294,7 +294,7 @@ "address": ["178.20.45.59"] }, { - "name": "Skywar.VN", - "address": ["nur-de-01.blued.host:25736"] + "name": "Mindus•VN", + "address": ["alpha.ateex.cloud:19172"] } ] From 9dd17ca16b885b8d032792713f1c1746932a02ec Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 23 Apr 2024 09:32:39 -0400 Subject: [PATCH 146/348] Moved drawBuilding to UnitType --- core/src/mindustry/entities/comp/BuilderComp.java | 4 ---- core/src/mindustry/type/UnitType.java | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/core/src/mindustry/entities/comp/BuilderComp.java b/core/src/mindustry/entities/comp/BuilderComp.java index 0c5070febb..df3692ee0e 100644 --- a/core/src/mindustry/entities/comp/BuilderComp.java +++ b/core/src/mindustry/entities/comp/BuilderComp.java @@ -290,10 +290,6 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{ return plans.size == 0 ? null : plans.first(); } - public void draw(){ - drawBuilding(); - } - public void drawBuilding(){ //TODO make this more generic so it works with builder "weapons" boolean active = activelyBuilding(); diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index 7ad45fad64..6734f1bc07 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -1193,6 +1193,8 @@ public class UnitType extends UnlockableContent implements Senseable{ public void draw(Unit unit){ if(unit.inFogTo(Vars.player.team())) return; + unit.drawBuilding(); + boolean isPayload = !unit.isAdded(); Mechc mech = unit instanceof Mechc ? (Mechc)unit : null; From 7d3c94de9bbbe61141dfdf90b8d263989a3d0a20 Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 23 Apr 2024 09:42:05 -0400 Subject: [PATCH 147/348] Moved drawMining to UnitType --- .../mindustry/entities/comp/MinerComp.java | 27 --------------- core/src/mindustry/type/UnitType.java | 34 ++++++++++++++++++- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/core/src/mindustry/entities/comp/MinerComp.java b/core/src/mindustry/entities/comp/MinerComp.java index 0e1caeb728..a58a0503c3 100644 --- a/core/src/mindustry/entities/comp/MinerComp.java +++ b/core/src/mindustry/entities/comp/MinerComp.java @@ -123,31 +123,4 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc{ } } } - - @Override - public void draw(){ - if(!mining()) return; - float focusLen = hitSize / 2f + Mathf.absin(Time.time, 1.1f, 0.5f); - float swingScl = 12f, swingMag = tilesize / 8f; - float flashScl = 0.3f; - - float px = x + Angles.trnsx(rotation, focusLen); - float py = y + Angles.trnsy(rotation, focusLen); - - float ex = mineTile.worldx() + Mathf.sin(Time.time + 48, swingScl, swingMag); - float ey = mineTile.worldy() + Mathf.sin(Time.time + 48, swingScl + 2f, swingMag); - - Draw.z(Layer.flyingUnit + 0.1f); - - Draw.color(Color.lightGray, Color.white, 1f - flashScl + Mathf.absin(Time.time, 0.5f, flashScl)); - - Drawf.laser(Core.atlas.find("minelaser"), Core.atlas.find("minelaser-end"), px, py, ex, ey, 0.75f); - - if(isLocal()){ - Lines.stroke(1f, Pal.accent); - Lines.poly(mineTile.worldx(), mineTile.worldy(), 4, tilesize / 2f * Mathf.sqrt2, Time.time); - } - - Draw.color(); - } } diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index 6734f1bc07..06d9f24e1f 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -429,7 +429,8 @@ public class UnitType extends UnlockableContent implements Senseable{ //(undocumented, you shouldn't need to use these, and if you do just check how they're drawn and copy that) public TextureRegion baseRegion, legRegion, region, previewRegion, shadowRegion, cellRegion, itemCircleRegion, - softShadowRegion, jointRegion, footRegion, legBaseRegion, baseJointRegion, outlineRegion, treadRegion; + softShadowRegion, jointRegion, footRegion, legBaseRegion, baseJointRegion, outlineRegion, treadRegion, + mineLaserRegion, mineLaserEndRegion; public TextureRegion[] wreckRegions, segmentRegions, segmentOutlineRegions; public TextureRegion[][] treadRegions; @@ -917,6 +918,9 @@ public class UnitType extends UnlockableContent implements Senseable{ legBaseRegion = Core.atlas.find(name + "-leg-base", name + "-leg"); baseRegion = Core.atlas.find(name + "-base"); cellRegion = Core.atlas.find(name + "-cell", Core.atlas.find("power-cell")); + + mineLaserRegion = Core.atlas.find("minelaser"); + mineLaserEndRegion = Core.atlas.find("minelaser-end"); //when linear filtering is on, it's acceptable to use the relatively low-res 'particle' region softShadowRegion = squareShape ? Core.atlas.find("square-shadow") : @@ -1195,6 +1199,8 @@ public class UnitType extends UnlockableContent implements Senseable{ unit.drawBuilding(); + drawMining(unit); + boolean isPayload = !unit.isAdded(); Mechc mech = unit instanceof Mechc ? (Mechc)unit : null; @@ -1298,6 +1304,32 @@ public class UnitType extends UnlockableContent implements Senseable{ Draw.reset(); } + public void drawMining(Unit unit){ + if(!unit.mining()) return; + float focusLen = unit.hitSize / 2f + Mathf.absin(Time.time, 1.1f, 0.5f); + float swingScl = 12f, swingMag = tilesize / 8f; + float flashScl = 0.3f; + + float px = unit.x + Angles.trnsx(unit.rotation, focusLen); + float py = unit.y + Angles.trnsy(unit.rotation, focusLen); + + float ex = unit.mineTile.worldx() + Mathf.sin(Time.time + 48, swingScl, swingMag); + float ey = unit.mineTile.worldy() + Mathf.sin(Time.time + 48, swingScl + 2f, swingMag); + + Draw.z(Layer.flyingUnit + 0.1f); + + Draw.color(Color.lightGray, Color.white, 1f - flashScl + Mathf.absin(Time.time, 0.5f, flashScl)); + + Drawf.laser(mineLaserRegion, mineLaserEndRegion, px, py, ex, ey, 0.75f); + + if(unit.isLocal()){ + Lines.stroke(1f, Pal.accent); + Lines.poly(unit.mineTile.worldx(), unit.mineTile.worldy(), 4, tilesize / 2f * Mathf.sqrt2, Time.time); + } + + Draw.color(); + } + public void drawPayload(T unit){ if(unit.hasPayload()){ Payload pay = unit.payloads().first(); From 0a50e7dc4859cc411b43cc2eb757a2ab2608406e Mon Sep 17 00:00:00 2001 From: NealRead <160118416+NealRead@users.noreply.github.com> Date: Wed, 24 Apr 2024 06:20:26 +0700 Subject: [PATCH 148/348] Update servers_v7.json (#9774) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 84a60822e0..0b7a1b9092 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -291,7 +291,7 @@ }, { "name": "NPTS", - "address": ["178.20.45.59"] + "address": ["81.94.150.170"] }, { "name": "Mindus•VN", From 76f85df33f78d5228983b6573c58b719f7c6f2a5 Mon Sep 17 00:00:00 2001 From: Leo-MathGuy Date: Wed, 24 Apr 2024 20:12:57 +0500 Subject: [PATCH 149/348] Added content order --- core/src/mindustry/mod/Mods.java | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java index 9e8e6b2b2b..251452162d 100644 --- a/core/src/mindustry/mod/Mods.java +++ b/core/src/mindustry/mod/Mods.java @@ -728,6 +728,9 @@ public class Mods implements Loadable{ Seq runs = new Seq<>(); for(LoadedMod mod : orderedMods()){ + ObjectMap currentRun = new ObjectMap<>(); + String[] contentOrder = mod.meta.contentOrder; + if(mod.root.child("content").exists()){ Fi contentRoot = mod.root.child("content"); for(ContentType type : ContentType.all){ @@ -735,15 +738,34 @@ public class Mods implements Loadable{ Fi folder = contentRoot.child(lower + (lower.endsWith("s") ? "" : "s")); if(folder.exists()){ for(Fi file : folder.findAll(f -> f.extension().equals("json") || f.extension().equals("hjson"))){ - runs.add(new LoadRun(type, file, mod)); + if(contentOrder == null){ + runs.add(new LoadRun(type, file, mod)); + }else{ + currentRun.put(file.nameWithoutExtension(), new LoadRun(type, file, mod)); + } } } } } + + Seq added = new Seq<>(); + if(contentOrder != null){ + for (String contentName : contentOrder){ + LoadRun run = currentRun.get(contentName); + if(run != null){ + runs.add(run); + added.add(contentName); + }else{ + Log.warn("Cannot find content defined in contentOrder: @", contentName); + } + } + } + + Seq left = currentRun.keys().toSeq(); + left.removeAll(added); + runs.addAll(left.map(name -> currentRun.get(name)).sort()); } - //make sure mod content is in proper order - runs.sort(); for(LoadRun l : runs){ Content current = content.getLastAdded(); try{ @@ -1210,6 +1232,8 @@ public class Mods implements Loadable{ public float texturescale = 1.0f; /** If true, bleeding is skipped and no content icons are generated. */ public boolean pregenerated; + /** If set, load the mod content in this order by content names */ + public String[] contentOrder; public String displayName(){ //useless, kept for legacy reasons From c2c9905998119e6f09573365a337cd87b25dcbe0 Mon Sep 17 00:00:00 2001 From: TheRadioactiveBanana <89061718+TheRadioactiveBanana@users.noreply.github.com> Date: Wed, 24 Apr 2024 22:20:27 +0530 Subject: [PATCH 150/348] Eradication Mindustry finally getting stable UK nodes (#9776) added new internet infrastructure and everything works flawlessly, also reserving ports for some new gamemodes that are planned --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 0b7a1b9092..73fc7afa98 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -146,7 +146,7 @@ }, { "name": "Eradication Mindustry", - "address": ["140.238.246.78:7000", "140.238.246.78:7001", "140.238.246.78:7002", "140.238.246.78:7003", "140.238.246.78:7004", "130.61.22.183:7000", "130.61.22.183:7001", "130.61.22.183:7002", "130.61.22.183:7003", "130.61.22.183:7004", "130.61.22.183:7005", "130.61.22.183:7006", "130.61.22.183:7007", "130.61.22.183:7008", "130.61.22.183:7009"] + "address": ["140.238.246.78:7000", "140.238.246.78:7001", "140.238.246.78:7002", "140.238.246.78:7003", "140.238.246.78:7004", "130.61.22.183:7000", "130.61.22.183:7001", "130.61.22.183:7002", "130.61.22.183:7003", "130.61.22.183:7004", "130.61.22.183:7005", "62.30.47.117:7000", "62.30.47.117:7001", "62.30.47.117:7002", "62.30.47.117:7003", "62.30.47.117:7004"] }, { "name": "Conservatory", From 7c69e555fdb862efbd94a88e3d3de23388d1455b Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 24 Apr 2024 20:17:16 -0400 Subject: [PATCH 151/348] Allow ram stance for any ground unit --- core/src/mindustry/type/UnitType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index 06d9f24e1f..2b50ff09fe 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -850,7 +850,7 @@ public class UnitType extends UnlockableContent implements Senseable{ if(stances.length == 0){ if(canAttack){ Seq seq = Seq.with(UnitStance.stop, UnitStance.shoot, UnitStance.holdFire, UnitStance.pursueTarget, UnitStance.patrol); - if(crushDamage > 0){ + if(!flying){ seq.add(UnitStance.ram); } stances = seq.toArray(UnitStance.class); From 7fcde1e109d5a9a1c858bac64e166e3ba8ceb201 Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 24 Apr 2024 22:40:20 -0400 Subject: [PATCH 152/348] Fixed derelict blocks not repairing when resources are not present --- core/src/mindustry/entities/comp/BuilderComp.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/entities/comp/BuilderComp.java b/core/src/mindustry/entities/comp/BuilderComp.java index df3692ee0e..794edf5d04 100644 --- a/core/src/mindustry/entities/comp/BuilderComp.java +++ b/core/src/mindustry/entities/comp/BuilderComp.java @@ -138,7 +138,11 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{ if(!(tile.build instanceof ConstructBuild cb)){ if(!current.initialized && !current.breaking && Build.validPlace(current.block, team, current.x, current.y, current.rotation)){ - boolean hasAll = infinite || current.isRotation(team) || !Structs.contains(current.block.requirements, i -> core != null && !core.items.has(i.item, Math.min(Mathf.round(i.amount * state.rules.buildCostMultiplier), 1))); + boolean hasAll = infinite || current.isRotation(team) || + //derelict repair + (tile.team() == Team.derelict && tile.block() == current.block && tile.build != null && tile.block().allowDerelictRepair && state.rules.derelictRepair) || + //make sure there's at least 1 item of each type first + !Structs.contains(current.block.requirements, i -> core != null && !core.items.has(i.item, Math.min(Mathf.round(i.amount * state.rules.buildCostMultiplier), 1))); if(hasAll){ Call.beginPlace(self(), current.block, team, current.x, current.y, current.rotation); From bac6f7cfa97eb3e826ea66c388a2710f327d2658 Mon Sep 17 00:00:00 2001 From: BlackDeluxeCat <65377021+BlackDeluxeCat@users.noreply.github.com> Date: Thu, 25 Apr 2024 22:23:13 +0800 Subject: [PATCH 153/348] Add logic filter (#7251) * logic filter * bundles * available in map editor * @this = null * generating only * ensure not using net --------- Co-authored-by: Anuken --- core/assets/bundles/bundle.properties | 3 + core/src/mindustry/logic/LExecutor.java | 4 +- core/src/mindustry/maps/Maps.java | 2 +- .../mindustry/maps/filters/LogicFilter.java | 82 +++++++++++++++++++ 4 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 core/src/mindustry/maps/filters/LogicFilter.java diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index cbf4f022d1..f7aeab2413 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -590,6 +590,7 @@ filter.clear = Clear filter.option.ignore = Ignore filter.scatter = Scatter filter.terrain = Terrain +filter.logic = Logic filter.option.scale = Scale filter.option.chance = Chance @@ -613,6 +614,8 @@ filter.option.floor2 = Secondary Floor filter.option.threshold2 = Secondary Threshold filter.option.radius = Radius filter.option.percentile = Percentile +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? diff --git a/core/src/mindustry/logic/LExecutor.java b/core/src/mindustry/logic/LExecutor.java index 91cb4575e2..c852d63981 100644 --- a/core/src/mindustry/logic/LExecutor.java +++ b/core/src/mindustry/logic/LExecutor.java @@ -60,7 +60,7 @@ public class LExecutor{ public @Nullable LogicBuild build; public IntSet linkIds = new IntSet(); public Team team = Team.derelict; - public boolean privileged = false; + public boolean privileged = false, isFilter = false; //yes, this is a minor memory leak, but it's probably not significant enough to matter protected static IntFloatMap unitTimeouts = new IntFloatMap(); @@ -1479,7 +1479,7 @@ public class LExecutor{ if(t == null) t = Team.derelict; if(tile.block() != b || tile.team() != t){ - tile.setNet(b, t, Mathf.clamp(exec.numi(rotation), 0, 3)); + tile.setBlock(b, t, Mathf.clamp(exec.numi(rotation), 0, 3)); } } } diff --git a/core/src/mindustry/maps/Maps.java b/core/src/mindustry/maps/Maps.java index 01bbde4b20..101a7447d7 100644 --- a/core/src/mindustry/maps/Maps.java +++ b/core/src/mindustry/maps/Maps.java @@ -33,7 +33,7 @@ public class Maps{ NoiseFilter::new, ScatterFilter::new, TerrainFilter::new, DistortFilter::new, RiverNoiseFilter::new, OreFilter::new, OreMedianFilter::new, MedianFilter::new, BlendFilter::new, MirrorFilter::new, ClearFilter::new, CoreSpawnFilter::new, - EnemySpawnFilter::new, SpawnPathFilter::new + EnemySpawnFilter::new, SpawnPathFilter::new, LogicFilter::new }; /** List of all built-in maps. Filenames only. */ diff --git a/core/src/mindustry/maps/filters/LogicFilter.java b/core/src/mindustry/maps/filters/LogicFilter.java new file mode 100644 index 0000000000..fc5f6129f0 --- /dev/null +++ b/core/src/mindustry/maps/filters/LogicFilter.java @@ -0,0 +1,82 @@ +package mindustry.maps.filters; + +import arc.scene.ui.layout.*; +import mindustry.gen.*; +import mindustry.logic.*; +import mindustry.maps.filters.FilterOption.*; +import mindustry.world.*; + +import static mindustry.Vars.*; + +public class LogicFilter extends GenerateFilter{ + /** max available execution for logic filter */ + public static int maxInstructionsExecution = 500 * 500 * 25; + public String code; + public boolean loop; + LExecutor executor; + + @Override + public FilterOption[] options(){ + return new FilterOption[]{ + new FilterOption(){ + final String name; + { + name = "code"; + } + @Override + public void build(Table table){ + table.button(b -> b.image(Icon.pencil).size(iconSmall), () -> { + ui.logic.show(code, null, true, code -> LogicFilter.this.code = code); + }).pad(4).margin(12f); + + table.add("@filter.option." + name); + } + }, + new ToggleOption("loop", () -> loop, f -> loop = f) + }; + } + + @Override + public void apply(Tiles tiles, GenerateInput in){ + executor = new LExecutor(); + executor.privileged = true; + executor.isFilter = true; + configure(code); + + //limited run + for(int i = 1; i < maxInstructionsExecution; i++){ + if(!loop && (executor.counter.numval >= executor.instructions.length || executor.counter.numval < 0)) break; + executor.runOnce(); + } + } + + @Override + public char icon(){ + return Iconc.blockMicroProcessor; + } + + @Override + public boolean isPost(){ + return true; + } + + void configure(String code){ + try{ + //create assembler to store extra variables + LAssembler asm = LAssembler.assemble(code, true); + + asm.putConst("@mapw", world.width()); + asm.putConst("@maph", world.height()); + asm.putConst("@links", executor.links.length); + asm.putConst("@ipt", 1); + + asm.putConst("@thisx", 0); + asm.putConst("@thisy", 0); + + executor.load(asm); + }catch(Exception e){ + //handle malformed code and replace it with nothing + executor.load(LAssembler.assemble(code = "", true)); + } + } +} From 304b62f0d55bff8e3b15e10544ac9b76d33a5eb0 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Thu, 25 Apr 2024 14:24:03 +0000 Subject: [PATCH 154/348] Automatic bundle update --- core/assets/bundles/bundle_be.properties | 3 +++ core/assets/bundles/bundle_bg.properties | 3 +++ core/assets/bundles/bundle_ca.properties | 3 +++ core/assets/bundles/bundle_cs.properties | 3 +++ core/assets/bundles/bundle_da.properties | 3 +++ core/assets/bundles/bundle_de.properties | 3 +++ core/assets/bundles/bundle_es.properties | 3 +++ core/assets/bundles/bundle_et.properties | 3 +++ core/assets/bundles/bundle_eu.properties | 3 +++ core/assets/bundles/bundle_fi.properties | 3 +++ core/assets/bundles/bundle_fil.properties | 3 +++ core/assets/bundles/bundle_fr.properties | 3 +++ core/assets/bundles/bundle_hu.properties | 3 +++ core/assets/bundles/bundle_id_ID.properties | 3 +++ core/assets/bundles/bundle_it.properties | 3 +++ core/assets/bundles/bundle_ja.properties | 3 +++ core/assets/bundles/bundle_ko.properties | 3 +++ core/assets/bundles/bundle_lt.properties | 3 +++ core/assets/bundles/bundle_nl.properties | 3 +++ core/assets/bundles/bundle_nl_BE.properties | 3 +++ core/assets/bundles/bundle_pl.properties | 3 +++ core/assets/bundles/bundle_pt_BR.properties | 3 +++ core/assets/bundles/bundle_pt_PT.properties | 3 +++ core/assets/bundles/bundle_ro.properties | 3 +++ core/assets/bundles/bundle_ru.properties | 3 +++ core/assets/bundles/bundle_sr.properties | 3 +++ core/assets/bundles/bundle_sv.properties | 3 +++ core/assets/bundles/bundle_th.properties | 3 +++ core/assets/bundles/bundle_tk.properties | 3 +++ core/assets/bundles/bundle_tr.properties | 3 +++ core/assets/bundles/bundle_uk_UA.properties | 3 +++ core/assets/bundles/bundle_vi.properties | 3 +++ core/assets/bundles/bundle_zh_CN.properties | 3 +++ core/assets/bundles/bundle_zh_TW.properties | 3 +++ 34 files changed, 102 insertions(+) diff --git a/core/assets/bundles/bundle_be.properties b/core/assets/bundles/bundle_be.properties index b1a94fc891..ef0161806b 100644 --- a/core/assets/bundles/bundle_be.properties +++ b/core/assets/bundles/bundle_be.properties @@ -576,6 +576,7 @@ filter.clear = Ачысціць filter.option.ignore = Ігнараваць filter.scatter = Сеяцель filter.terrain = Ландшафт +filter.logic = Logic filter.option.scale = Маштаб фільтра filter.option.chance = Шанец filter.option.mag = Сіла прымянення @@ -598,6 +599,8 @@ filter.option.floor2 = Другая паверхню filter.option.threshold2 = Другасны гранічны парог filter.option.radius = Радыус filter.option.percentile = Процентль +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_bg.properties b/core/assets/bundles/bundle_bg.properties index eafedcdd6b..64a898ce26 100644 --- a/core/assets/bundles/bundle_bg.properties +++ b/core/assets/bundles/bundle_bg.properties @@ -582,6 +582,7 @@ filter.clear = Изчисти filter.option.ignore = Игнорирай filter.scatter = Разпръскване filter.terrain = Терен +filter.logic = Logic filter.option.scale = Мащаб filter.option.chance = Вероятност filter.option.mag = Магнитут @@ -604,6 +605,8 @@ filter.option.floor2 = Втори под filter.option.threshold2 = Втори праг filter.option.radius = Радиус filter.option.percentile = Перцентил +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index 149756ce51..8a6d9ccbb0 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -584,6 +584,7 @@ filter.clear = Neteja filter.option.ignore = Ignora filter.scatter = Dispersió filter.terrain = Terreny +filter.logic = Logic filter.option.scale = Escala filter.option.chance = Probabilitat @@ -607,6 +608,8 @@ filter.option.floor2 = Terra secundari filter.option.threshold2 = Llindar secundari filter.option.radius = Radi filter.option.percentile = Percentil +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_cs.properties b/core/assets/bundles/bundle_cs.properties index 60ea6f6682..eb5d899226 100644 --- a/core/assets/bundles/bundle_cs.properties +++ b/core/assets/bundles/bundle_cs.properties @@ -583,6 +583,7 @@ filter.clear = Vyčistit filter.option.ignore = Ignorovat filter.scatter = Rozptýlení filter.terrain = Terén +filter.logic = Logic filter.option.scale = Měřítko filter.option.chance = Náhoda @@ -606,6 +607,8 @@ filter.option.floor2 = Druhotný povrch filter.option.threshold2 = Druhotný práh filter.option.radius = Poloměr filter.option.percentile = Percentil +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_da.properties b/core/assets/bundles/bundle_da.properties index cb118a0b54..599894cbb9 100644 --- a/core/assets/bundles/bundle_da.properties +++ b/core/assets/bundles/bundle_da.properties @@ -577,6 +577,7 @@ filter.clear = Ryd filter.option.ignore = Ignorer filter.scatter = Spreder filter.terrain = Terræn +filter.logic = Logic filter.option.scale = Skaler filter.option.chance = Chance filter.option.mag = Størrelse @@ -599,6 +600,8 @@ filter.option.floor2 = Sekundært gulv filter.option.threshold2 = Sekundær terskel filter.option.radius = Radius filter.option.percentile = Percentil +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_de.properties b/core/assets/bundles/bundle_de.properties index 4713fb1e5a..589339ee81 100644 --- a/core/assets/bundles/bundle_de.properties +++ b/core/assets/bundles/bundle_de.properties @@ -587,6 +587,7 @@ filter.clear = Löschen filter.option.ignore = Ignorieren filter.scatter = Streuen filter.terrain = Landschaft +filter.logic = Logic filter.option.scale = Skalierung filter.option.chance = Wahrscheinlichkeit @@ -610,6 +611,8 @@ filter.option.floor2 = Sekundärer Boden filter.option.threshold2 = Sekundärer Grenzwert filter.option.radius = Radius filter.option.percentile = Perzentil +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index 9200ef1e0a..987e4c5140 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -584,6 +584,7 @@ filter.clear = Despejar filter.option.ignore = Ignorar filter.scatter = Dispersión filter.terrain = Terreno +filter.logic = Logic filter.option.scale = Escala filter.option.chance = Probabilidad @@ -607,6 +608,8 @@ filter.option.floor2 = Terreno secundario filter.option.threshold2 = Umbral secundario filter.option.radius = Radio filter.option.percentile = Percentil +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_et.properties b/core/assets/bundles/bundle_et.properties index 6e5dc5c842..9f51bdf81e 100644 --- a/core/assets/bundles/bundle_et.properties +++ b/core/assets/bundles/bundle_et.properties @@ -577,6 +577,7 @@ filter.clear = Kustutamine filter.option.ignore = Eira filter.scatter = Puistamine filter.terrain = Maastik +filter.logic = Logic filter.option.scale = Ulatus filter.option.chance = Tõenäosus filter.option.mag = Suurusjärk @@ -599,6 +600,8 @@ filter.option.floor2 = Teine põrand filter.option.threshold2 = Teine lävi filter.option.radius = Raadius filter.option.percentile = Protsentiil +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_eu.properties b/core/assets/bundles/bundle_eu.properties index 20dcfa9f68..e2a0ed81fe 100644 --- a/core/assets/bundles/bundle_eu.properties +++ b/core/assets/bundles/bundle_eu.properties @@ -579,6 +579,7 @@ filter.clear = Garbitu filter.option.ignore = Ezikusi filter.scatter = Sakabanaketa filter.terrain = Lursaila +filter.logic = Logic filter.option.scale = Eskala filter.option.chance = Zoria filter.option.mag = Magnitudea @@ -601,6 +602,8 @@ filter.option.floor2 = Bigarren zorua filter.option.threshold2 = Bigarren atalasea filter.option.radius = Erradioa filter.option.percentile = Pertzentila +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_fi.properties b/core/assets/bundles/bundle_fi.properties index b81ad76e72..407e7121ac 100644 --- a/core/assets/bundles/bundle_fi.properties +++ b/core/assets/bundles/bundle_fi.properties @@ -577,6 +577,7 @@ filter.clear = Selkeä filter.option.ignore = Ohitta filter.scatter = Hajauta filter.terrain = Maasto +filter.logic = Logic filter.option.scale = Mittakaava filter.option.chance = Mahdollisuus filter.option.mag = Suuruus @@ -599,6 +600,8 @@ filter.option.floor2 = Toinen lattia filter.option.threshold2 = Toissijainen raja-arvo filter.option.radius = Säde filter.option.percentile = Prosentti +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_fil.properties b/core/assets/bundles/bundle_fil.properties index 18b9fd2aab..6fd0ac002a 100644 --- a/core/assets/bundles/bundle_fil.properties +++ b/core/assets/bundles/bundle_fil.properties @@ -577,6 +577,7 @@ filter.clear = Clear filter.option.ignore = Ignore filter.scatter = Scatter filter.terrain = Terrain +filter.logic = Logic filter.option.scale = Scale filter.option.chance = Chance filter.option.mag = Magnitude @@ -599,6 +600,8 @@ filter.option.floor2 = Secondary Floor filter.option.threshold2 = Secondary Threshold filter.option.radius = Radius filter.option.percentile = Percentile +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index 3ff26c05c4..818c8996d3 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -590,6 +590,7 @@ filter.clear = Effacer filter.option.ignore = Ignorer filter.scatter = Disperser filter.terrain = Terrain +filter.logic = Logic filter.option.scale = Échelle filter.option.chance = Chance @@ -613,6 +614,8 @@ filter.option.floor2 = Sol secondaire filter.option.threshold2 = Seuil secondaire filter.option.radius = Rayon filter.option.percentile = Pourcentage +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index 092ccd0465..69d119e5a4 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -590,6 +590,7 @@ filter.clear = Törlés filter.option.ignore = Elutasítás filter.scatter = Szétszórás filter.terrain = Domborzat +filter.logic = Logic filter.option.scale = Méretezés filter.option.chance = Gyakoriság @@ -613,6 +614,8 @@ filter.option.floor2 = Másodlagos talaj filter.option.threshold2 = Másodlagos küszöbérték filter.option.radius = Sugár filter.option.percentile = Százalék +filter.option.code = Code +filter.option.loop = Loop locales.info = Itt adhatsz hozzá különböző nyelvi csomagokat a pályádhoz. A nyelvi csomagokban minden tulajdonságnak van egy neve és egy értéke. Ezeket a tulajdonságokat a világprocesszorok és a célkitűzések is használhatják a saját neveikkel. Támogatják a szövegformázást (a helyőrzőket a tényleges értékükkel helyettesítik).\n\n[cyan]Példa tulajdonság:\n[]name: [accent]időzítő[]\nvalue: [accent]Példa időzítő, hátralévő idő: {0}[]\n\n[cyan]Használat:\n[]Beállítás célkitűzés szövegeként: [accent]@időzítő\n\n[]Írd be egy világprocesszorba:\n[accent]localeprint "időzítő"\nformat time\n[gray](ahol az idő egy külön számított változó) locales.deletelocale = Biztos, hogy törölni akarod ezt a nyelvi csomagot? diff --git a/core/assets/bundles/bundle_id_ID.properties b/core/assets/bundles/bundle_id_ID.properties index e42084a3a7..d0dfa77403 100644 --- a/core/assets/bundles/bundle_id_ID.properties +++ b/core/assets/bundles/bundle_id_ID.properties @@ -584,6 +584,7 @@ filter.clear = Bersih filter.option.ignore = Biarkan filter.scatter = Penebaran filter.terrain = Lahan +filter.logic = Logic filter.option.scale = Ukuran filter.option.chance = Kemungkinan @@ -607,6 +608,8 @@ filter.option.floor2 = Lantai Sekunder filter.option.threshold2 = Ambang Sekunder filter.option.radius = Radius filter.option.percentile = Perseratus +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties index 6383c512ca..6d11a604e9 100644 --- a/core/assets/bundles/bundle_it.properties +++ b/core/assets/bundles/bundle_it.properties @@ -580,6 +580,7 @@ filter.clear = Resetta Filtro filter.option.ignore = Ignora filter.scatter = Dispersione filter.terrain = Terreno +filter.logic = Logic filter.option.scale = Scala filter.option.chance = Probabilità filter.option.mag = Magnitudine @@ -602,6 +603,8 @@ filter.option.floor2 = Terreno Secondario filter.option.threshold2 = Soglia Secondaria filter.option.radius = Raggio filter.option.percentile = Percentuale +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_ja.properties b/core/assets/bundles/bundle_ja.properties index ed0dc7cc69..4666e41ede 100644 --- a/core/assets/bundles/bundle_ja.properties +++ b/core/assets/bundles/bundle_ja.properties @@ -583,6 +583,7 @@ filter.clear = クリア filter.option.ignore = 無視 filter.scatter = 分散 filter.terrain = 地形 +filter.logic = Logic filter.option.scale = スケール filter.option.chance = 確率 @@ -606,6 +607,8 @@ filter.option.floor2 = 2番目の地面 filter.option.threshold2 = 2番目の閾値 filter.option.radius = 半径 filter.option.percentile = パーセンタイル +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index 2ec87a4548..6b7c26a06d 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -583,6 +583,7 @@ filter.clear = 초기화 filter.option.ignore = 무시 filter.scatter = 흩뿌리기 filter.terrain = 지형 +filter.logic = Logic filter.option.scale = 크기 filter.option.chance = 배치 빈도 @@ -606,6 +607,8 @@ filter.option.floor2 = 2번째 타일 filter.option.threshold2 = 2번째 경계선 filter.option.radius = 반경 filter.option.percentile = 백분율 +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_lt.properties b/core/assets/bundles/bundle_lt.properties index 90cb6efbf4..8769614258 100644 --- a/core/assets/bundles/bundle_lt.properties +++ b/core/assets/bundles/bundle_lt.properties @@ -577,6 +577,7 @@ filter.clear = Išvalyti filter.option.ignore = ignoruoti filter.scatter = Išsklaidyti filter.terrain = Reljefas +filter.logic = Logic filter.option.scale = Mastelis filter.option.chance = Tikimybė filter.option.mag = Didumas @@ -599,6 +600,8 @@ filter.option.floor2 = Antrasis sluoksnis filter.option.threshold2 = Antrasis slenkstis filter.option.radius = Spindulys filter.option.percentile = Procentilė +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_nl.properties b/core/assets/bundles/bundle_nl.properties index fb69dc8371..94a56a1ff3 100644 --- a/core/assets/bundles/bundle_nl.properties +++ b/core/assets/bundles/bundle_nl.properties @@ -586,6 +586,7 @@ filter.clear = Verwijder filter.option.ignore = Negeer filter.scatter = Verstrooi filter.terrain = Terrein +filter.logic = Logic filter.option.scale = Schaal filter.option.chance = Verander @@ -609,6 +610,8 @@ filter.option.floor2 = Secundaire Vloer filter.option.threshold2 = Secundaire Drempel filter.option.radius = Straal filter.option.percentile = percentiel +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_nl_BE.properties b/core/assets/bundles/bundle_nl_BE.properties index 1f345b97e0..44e2eaf603 100644 --- a/core/assets/bundles/bundle_nl_BE.properties +++ b/core/assets/bundles/bundle_nl_BE.properties @@ -577,6 +577,7 @@ filter.clear = Clear filter.option.ignore = Ignore filter.scatter = Scatter filter.terrain = Terrain +filter.logic = Logic filter.option.scale = Scale filter.option.chance = Chance filter.option.mag = Magnitude @@ -599,6 +600,8 @@ filter.option.floor2 = Secondary Floor filter.option.threshold2 = Secondary Threshold filter.option.radius = Radius filter.option.percentile = Percentile +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_pl.properties b/core/assets/bundles/bundle_pl.properties index 53e9cf4317..ca8fcc8028 100644 --- a/core/assets/bundles/bundle_pl.properties +++ b/core/assets/bundles/bundle_pl.properties @@ -582,6 +582,7 @@ filter.clear = Oczyść filter.option.ignore = Ignoruj filter.scatter = Rozprosz filter.terrain = Teren +filter.logic = Logic filter.option.scale = Skala filter.option.chance = Szansa filter.option.mag = Wielkość @@ -604,6 +605,8 @@ filter.option.floor2 = Druga Podłoga filter.option.threshold2 = Drugi Próg filter.option.radius = Zasięg filter.option.percentile = Procent +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index 4ba31c527b..f3327bcb00 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -584,6 +584,7 @@ filter.clear = Excluir filter.option.ignore = Ignorar filter.scatter = Dispersão filter.terrain = Terreno +filter.logic = Logic filter.option.scale = Escala filter.option.chance = Chance @@ -607,6 +608,8 @@ filter.option.floor2 = Chão secundário filter.option.threshold2 = Margem secundária filter.option.radius = Raio filter.option.percentile = Percentual +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_pt_PT.properties b/core/assets/bundles/bundle_pt_PT.properties index 1780792fe3..f20c6a6a9f 100644 --- a/core/assets/bundles/bundle_pt_PT.properties +++ b/core/assets/bundles/bundle_pt_PT.properties @@ -577,6 +577,7 @@ filter.clear = Excluir filter.option.ignore = Ignorar filter.scatter = Dispersão filter.terrain = Terreno +filter.logic = Logic filter.option.scale = Escala filter.option.chance = Chance filter.option.mag = Magnitude @@ -599,6 +600,8 @@ filter.option.floor2 = Chão secundário filter.option.threshold2 = Margem secundária filter.option.radius = Raio filter.option.percentile = Percentual +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_ro.properties b/core/assets/bundles/bundle_ro.properties index b2db9b66cd..34856c9bf1 100644 --- a/core/assets/bundles/bundle_ro.properties +++ b/core/assets/bundles/bundle_ro.properties @@ -583,6 +583,7 @@ filter.clear = Curăță filter.option.ignore = Ignoră filter.scatter = Împrăștie filter.terrain = Teren +filter.logic = Logic filter.option.scale = Scară filter.option.chance = Șansă @@ -606,6 +607,8 @@ filter.option.floor2 = Podea Secundară filter.option.threshold2 = Cantitate Secundară filter.option.radius = Rază filter.option.percentile = Procent +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index ae8defc92e..39a709f1a2 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -583,6 +583,7 @@ filter.clear = Очистить filter.option.ignore = Игнорировать filter.scatter = Сеятель filter.terrain = Ландшафт +filter.logic = Logic filter.option.scale = Масштаб фильтра filter.option.chance = Шанс @@ -606,6 +607,8 @@ filter.option.floor2 = Вторая поверхность filter.option.threshold2 = Вторичный предельный порог filter.option.radius = Радиус filter.option.percentile = Процентиль +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_sr.properties b/core/assets/bundles/bundle_sr.properties index 57e10040b7..34f7013628 100644 --- a/core/assets/bundles/bundle_sr.properties +++ b/core/assets/bundles/bundle_sr.properties @@ -583,6 +583,7 @@ filter.clear = Očisti filter.option.ignore = Ignoriši filter.scatter = Razbaci filter.terrain = Teren +filter.logic = Logic filter.option.scale = Razmera filter.option.chance = Šansa @@ -606,6 +607,8 @@ filter.option.floor2 = Drugi Pod filter.option.threshold2 = Secondary Threshold filter.option.radius = Radius filter.option.percentile = Percentile +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_sv.properties b/core/assets/bundles/bundle_sv.properties index 2268e25ca8..39f7ef5245 100644 --- a/core/assets/bundles/bundle_sv.properties +++ b/core/assets/bundles/bundle_sv.properties @@ -577,6 +577,7 @@ filter.clear = Rensa filter.option.ignore = Ignorera filter.scatter = Sprid filter.terrain = Terräng +filter.logic = Logic filter.option.scale = Skala filter.option.chance = Chans filter.option.mag = Magnitud @@ -599,6 +600,8 @@ filter.option.floor2 = Secondary Floor filter.option.threshold2 = Secondary Threshold filter.option.radius = Radie filter.option.percentile = Percentile +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_th.properties b/core/assets/bundles/bundle_th.properties index 91573f01ba..839e1afcc9 100644 --- a/core/assets/bundles/bundle_th.properties +++ b/core/assets/bundles/bundle_th.properties @@ -583,6 +583,7 @@ filter.clear = เคลียร์ filter.option.ignore = เพิกเฉย filter.scatter = กระจาย filter.terrain = พื้นผิว +filter.logic = Logic filter.option.scale = มาตราส่วน filter.option.chance = โอกาส @@ -606,6 +607,8 @@ filter.option.floor2 = พื้นชั้นสอง filter.option.threshold2 = เกณฑ์ชั้นสอง filter.option.radius = รัศมี filter.option.percentile = เปอร์เซ็นต์ไทล์ +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_tk.properties b/core/assets/bundles/bundle_tk.properties index f2922470e8..7cd62351f8 100644 --- a/core/assets/bundles/bundle_tk.properties +++ b/core/assets/bundles/bundle_tk.properties @@ -577,6 +577,7 @@ filter.clear = Clear filter.option.ignore = Ignore filter.scatter = Scatter filter.terrain = Terrain +filter.logic = Logic filter.option.scale = Scale filter.option.chance = Chance filter.option.mag = Magnitude @@ -599,6 +600,8 @@ filter.option.floor2 = Secondary Floor filter.option.threshold2 = Secondary Threshold filter.option.radius = Radius filter.option.percentile = Percentile +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_tr.properties b/core/assets/bundles/bundle_tr.properties index 9a9ecead53..927c429724 100644 --- a/core/assets/bundles/bundle_tr.properties +++ b/core/assets/bundles/bundle_tr.properties @@ -583,6 +583,7 @@ filter.clear = Temizle filter.option.ignore = Yoksay filter.scatter = Saç filter.terrain = Arazi +filter.logic = Logic filter.option.scale = Ölçek filter.option.chance = Şans @@ -606,6 +607,8 @@ filter.option.floor2 = İkincil Duvar filter.option.threshold2 = İkincil Eşik filter.option.radius = Yarıçap filter.option.percentile = Yüzdelik +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 24a8cf4738..5922ff8742 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -586,6 +586,7 @@ filter.clear = Очистити filter.option.ignore = Ігнорувати filter.scatter = Розсіювач filter.terrain = Ландшафт +filter.logic = Logic filter.option.scale = Масштаб фільтра filter.option.chance = Шанс @@ -609,6 +610,8 @@ filter.option.floor2 = Друга поверхня filter.option.threshold2 = Вторинний граничний поріг filter.option.radius = Радіус filter.option.percentile = Спад +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index 7856f9339e..6b25d60051 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -590,6 +590,7 @@ filter.clear = Xóa filter.option.ignore = Phớt lờ filter.scatter = Phân tán filter.terrain = Địa hình +filter.logic = Logic filter.option.scale = Kích thước filter.option.chance = Tỷ lệ @@ -613,6 +614,8 @@ filter.option.floor2 = Nền phụ filter.option.threshold2 = Ngưỡng phụ filter.option.radius = Bán kính filter.option.percentile = Phần trăm +filter.option.code = Code +filter.option.loop = Loop locales.info = Tại đây, bạn có thể thêm gói ngôn ngữ cho ngôn ngữ cụ thể vào bản đồ của bạn. Trong gói ngôn ngữ, mỗi thuộc tính có tên và giá trị. Những thuộc tính này có thể được sử dụng bởi Bộ xử lý thế giới và Mục tiêu nhiệm vụ bằng tên của chúng. Chúng hỗ trợ định dạng văn bản (thay thế kí tự giữ chỗ bằng giá trị thực tế).\n\n[cyan]Ví dụ thuộc tính:\n[]tên: [accent]timer[]\nvalue: [accent]Bộ đếm thời gian ví dụ, thời gian còn lại: @[]\n\n[cyan]Cách dùng:\n[]Đặt làm văn bản cho Mục tiêu nhiệm vụ: [accent]@timer\n\n[]In nó trong Bộ xử lý thế giới:\n[accent]localeprint "timer"\nformat time\n[gray](với time là biến được tính toán riêng biệt) locales.deletelocale = Bạn có chắc muốn xóa gói ngôn ngữ này? diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index a908d005a5..ff4c1e80f1 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -587,6 +587,7 @@ filter.clear = 替换 filter.option.ignore = 忽略 filter.scatter = 散布 filter.terrain = 地图边界 +filter.logic = Logic filter.option.scale = 缩放 filter.option.chance = 散布数量 @@ -610,6 +611,8 @@ filter.option.floor2 = 内层地形 filter.option.threshold2 = 内层比例 filter.option.radius = 半径 filter.option.percentile = 百分比 +filter.option.code = Code +filter.option.loop = Loop locales.info = 在这里,您可以为特定语言添加本地化语言包到您的地图中。在本地化语言包中,每个文本属性都有一个名称和一个值。这些文本属性可以由世界处理器和游戏目标使用它们的名称。它们支持文本格式化(用实际值替换占位符)。\n\n[cyan]示例文本属性:\n[]名称: [accent]timer[]值: [accent]示例计时器, 剩余时间: {0}[]\n\n[cyan]用法:\n[]将其设置为目标的文本: [accent]@timer\n\n[]在世界处理器中打印它:\n[accent]localeprint "timer"\n格式化时间\n[gray](时间是一个单独计算的变量) locales.deletelocale = 您确定要删除该本地化语言包吗? locales.applytoall = 将更改应用于所有本地化语言包 diff --git a/core/assets/bundles/bundle_zh_TW.properties b/core/assets/bundles/bundle_zh_TW.properties index 049f543412..cf18d59d5d 100644 --- a/core/assets/bundles/bundle_zh_TW.properties +++ b/core/assets/bundles/bundle_zh_TW.properties @@ -584,6 +584,7 @@ filter.clear = 清除 filter.option.ignore = 忽略 filter.scatter = 分散 filter.terrain = 地形 +filter.logic = Logic filter.option.scale = 規模 filter.option.chance = 機會 @@ -607,6 +608,8 @@ filter.option.floor2 = 次要地板 filter.option.threshold2 = 次要閾值 filter.option.radius = 半徑 filter.option.percentile = 百分比 +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? locales.applytoall = Apply Changes To All Locales From be64defc377bbfda04df76ae9356dfd4f3685bcc Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 25 Apr 2024 10:34:38 -0400 Subject: [PATCH 155/348] Logic filter cleanup --- core/src/mindustry/logic/LExecutor.java | 2 +- .../mindustry/maps/filters/LogicFilter.java | 36 ++++++++----------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/core/src/mindustry/logic/LExecutor.java b/core/src/mindustry/logic/LExecutor.java index c852d63981..a0cb26a57c 100644 --- a/core/src/mindustry/logic/LExecutor.java +++ b/core/src/mindustry/logic/LExecutor.java @@ -60,7 +60,7 @@ public class LExecutor{ public @Nullable LogicBuild build; public IntSet linkIds = new IntSet(); public Team team = Team.derelict; - public boolean privileged = false, isFilter = false; + public boolean privileged = false; //yes, this is a minor memory leak, but it's probably not significant enough to matter protected static IntFloatMap unitTimeouts = new IntFloatMap(); diff --git a/core/src/mindustry/maps/filters/LogicFilter.java b/core/src/mindustry/maps/filters/LogicFilter.java index fc5f6129f0..6b53bb1636 100644 --- a/core/src/mindustry/maps/filters/LogicFilter.java +++ b/core/src/mindustry/maps/filters/LogicFilter.java @@ -1,6 +1,7 @@ package mindustry.maps.filters; import arc.scene.ui.layout.*; +import mindustry.*; import mindustry.gen.*; import mindustry.logic.*; import mindustry.maps.filters.FilterOption.*; @@ -40,8 +41,19 @@ public class LogicFilter extends GenerateFilter{ public void apply(Tiles tiles, GenerateInput in){ executor = new LExecutor(); executor.privileged = true; - executor.isFilter = true; - configure(code); + + try{ + //assembler has no variables, all the standard ones are null + executor.load(LAssembler.assemble(code, true)); + }catch(Throwable ignored){ + //if loading code + return; + } + + //this updates map width/height global variables + logicVars.update(); + + //NOTE: all tile operations will call setNet for tiles, but that should have no overhead during world loading //limited run for(int i = 1; i < maxInstructionsExecution; i++){ @@ -59,24 +71,4 @@ public class LogicFilter extends GenerateFilter{ public boolean isPost(){ return true; } - - void configure(String code){ - try{ - //create assembler to store extra variables - LAssembler asm = LAssembler.assemble(code, true); - - asm.putConst("@mapw", world.width()); - asm.putConst("@maph", world.height()); - asm.putConst("@links", executor.links.length); - asm.putConst("@ipt", 1); - - asm.putConst("@thisx", 0); - asm.putConst("@thisy", 0); - - executor.load(asm); - }catch(Exception e){ - //handle malformed code and replace it with nothing - executor.load(LAssembler.assemble(code = "", true)); - } - } } From a07d1f2c00772f2fa69a924e24c3d5d97006bc16 Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 25 Apr 2024 10:36:41 -0400 Subject: [PATCH 156/348] More cleanup --- core/src/mindustry/maps/filters/LogicFilter.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/src/mindustry/maps/filters/LogicFilter.java b/core/src/mindustry/maps/filters/LogicFilter.java index 6b53bb1636..1346d1a52e 100644 --- a/core/src/mindustry/maps/filters/LogicFilter.java +++ b/core/src/mindustry/maps/filters/LogicFilter.java @@ -14,7 +14,6 @@ public class LogicFilter extends GenerateFilter{ public static int maxInstructionsExecution = 500 * 500 * 25; public String code; public boolean loop; - LExecutor executor; @Override public FilterOption[] options(){ @@ -39,7 +38,7 @@ public class LogicFilter extends GenerateFilter{ @Override public void apply(Tiles tiles, GenerateInput in){ - executor = new LExecutor(); + LExecutor executor = new LExecutor(); executor.privileged = true; try{ @@ -54,8 +53,7 @@ public class LogicFilter extends GenerateFilter{ logicVars.update(); //NOTE: all tile operations will call setNet for tiles, but that should have no overhead during world loading - - //limited run + //executions are limited to prevent infinite generation for(int i = 1; i < maxInstructionsExecution; i++){ if(!loop && (executor.counter.numval >= executor.instructions.length || executor.counter.numval < 0)) break; executor.runOnce(); From 1622ecd0105d1998994a408c69f44d61ed0e7d62 Mon Sep 17 00:00:00 2001 From: Leo-MathGuy Date: Thu, 25 Apr 2024 19:41:05 +0500 Subject: [PATCH 157/348] Fixed double seq --- core/src/mindustry/mod/Mods.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java index 251452162d..c31f648637 100644 --- a/core/src/mindustry/mod/Mods.java +++ b/core/src/mindustry/mod/Mods.java @@ -748,21 +748,19 @@ public class Mods implements Loadable{ } } - Seq added = new Seq<>(); + Seq left = currentRun.keys().toSeq(); if(contentOrder != null){ for (String contentName : contentOrder){ LoadRun run = currentRun.get(contentName); if(run != null){ runs.add(run); - added.add(contentName); + left.remove(contentName); }else{ Log.warn("Cannot find content defined in contentOrder: @", contentName); } } } - Seq left = currentRun.keys().toSeq(); - left.removeAll(added); runs.addAll(left.map(name -> currentRun.get(name)).sort()); } From 617953dd89dd99999f5e899de74590d975715652 Mon Sep 17 00:00:00 2001 From: Leo-MathGuy Date: Thu, 25 Apr 2024 19:44:44 +0500 Subject: [PATCH 158/348] format --- core/src/mindustry/mod/Mods.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java index c31f648637..0e5580a944 100644 --- a/core/src/mindustry/mod/Mods.java +++ b/core/src/mindustry/mod/Mods.java @@ -738,11 +738,7 @@ public class Mods implements Loadable{ Fi folder = contentRoot.child(lower + (lower.endsWith("s") ? "" : "s")); if(folder.exists()){ for(Fi file : folder.findAll(f -> f.extension().equals("json") || f.extension().equals("hjson"))){ - if(contentOrder == null){ - runs.add(new LoadRun(type, file, mod)); - }else{ - currentRun.put(file.nameWithoutExtension(), new LoadRun(type, file, mod)); - } + currentRun.put(file.nameWithoutExtension(), new LoadRun(type, file, mod)); } } } From 56c68fef9ccdbeca4a147a0d73c10d5b9fbe9b0e Mon Sep 17 00:00:00 2001 From: Leo-MathGuy Date: Thu, 25 Apr 2024 19:55:51 +0500 Subject: [PATCH 159/348] how did a format change make the test fail --- core/src/mindustry/mod/Mods.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java index 0e5580a944..98caf3c8b3 100644 --- a/core/src/mindustry/mod/Mods.java +++ b/core/src/mindustry/mod/Mods.java @@ -744,8 +744,8 @@ public class Mods implements Loadable{ } } - Seq left = currentRun.keys().toSeq(); if(contentOrder != null){ + Seq left = currentRun.keys().toSeq(); for (String contentName : contentOrder){ LoadRun run = currentRun.get(contentName); if(run != null){ @@ -755,9 +755,10 @@ public class Mods implements Loadable{ Log.warn("Cannot find content defined in contentOrder: @", contentName); } } + runs.addAll(left.map(name -> currentRun.get(name)).sort()); + }else{ + runs.addAll(currentRun.values().toSeq().sort()); } - - runs.addAll(left.map(name -> currentRun.get(name)).sort()); } for(LoadRun l : runs){ From d9cc2ef9a4fd806ce9fb858a949c873dcb728f96 Mon Sep 17 00:00:00 2001 From: Leo-MathGuy Date: Thu, 25 Apr 2024 19:59:08 +0500 Subject: [PATCH 160/348] sorting may be the problem --- core/src/mindustry/mod/Mods.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java index 98caf3c8b3..53acdc91c9 100644 --- a/core/src/mindustry/mod/Mods.java +++ b/core/src/mindustry/mod/Mods.java @@ -755,9 +755,9 @@ public class Mods implements Loadable{ Log.warn("Cannot find content defined in contentOrder: @", contentName); } } - runs.addAll(left.map(name -> currentRun.get(name)).sort()); + runs.addAll(left.map(name -> currentRun.get(name))); }else{ - runs.addAll(currentRun.values().toSeq().sort()); + runs.addAll(currentRun.values().toSeq()); } } From 0a7a3dc295ca1490f26d7681b4e1a330d36008dd Mon Sep 17 00:00:00 2001 From: Leo-MathGuy Date: Thu, 25 Apr 2024 20:03:38 +0500 Subject: [PATCH 161/348] Revert 3 commits --- core/src/mindustry/mod/Mods.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java index 53acdc91c9..c31f648637 100644 --- a/core/src/mindustry/mod/Mods.java +++ b/core/src/mindustry/mod/Mods.java @@ -738,14 +738,18 @@ public class Mods implements Loadable{ Fi folder = contentRoot.child(lower + (lower.endsWith("s") ? "" : "s")); if(folder.exists()){ for(Fi file : folder.findAll(f -> f.extension().equals("json") || f.extension().equals("hjson"))){ - currentRun.put(file.nameWithoutExtension(), new LoadRun(type, file, mod)); + if(contentOrder == null){ + runs.add(new LoadRun(type, file, mod)); + }else{ + currentRun.put(file.nameWithoutExtension(), new LoadRun(type, file, mod)); + } } } } } + Seq left = currentRun.keys().toSeq(); if(contentOrder != null){ - Seq left = currentRun.keys().toSeq(); for (String contentName : contentOrder){ LoadRun run = currentRun.get(contentName); if(run != null){ @@ -755,10 +759,9 @@ public class Mods implements Loadable{ Log.warn("Cannot find content defined in contentOrder: @", contentName); } } - runs.addAll(left.map(name -> currentRun.get(name))); - }else{ - runs.addAll(currentRun.values().toSeq()); } + + runs.addAll(left.map(name -> currentRun.get(name)).sort()); } for(LoadRun l : runs){ From d3dc934a74fa7732848517bad5a830e5f61227a0 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 26 Apr 2024 10:15:16 -0400 Subject: [PATCH 162/348] Fixed #9784 --- core/src/mindustry/maps/filters/BlendFilter.java | 2 +- core/src/mindustry/maps/filters/SpawnPathFilter.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/maps/filters/BlendFilter.java b/core/src/mindustry/maps/filters/BlendFilter.java index 70b3b0408b..4f1c5bfaba 100644 --- a/core/src/mindustry/maps/filters/BlendFilter.java +++ b/core/src/mindustry/maps/filters/BlendFilter.java @@ -27,7 +27,7 @@ public class BlendFilter extends GenerateFilter{ @Override public char icon(){ - return Iconc.blockSand; + return Iconc.blockSandFloor; } @Override diff --git a/core/src/mindustry/maps/filters/SpawnPathFilter.java b/core/src/mindustry/maps/filters/SpawnPathFilter.java index daf1ea8aea..e02ab4d02a 100644 --- a/core/src/mindustry/maps/filters/SpawnPathFilter.java +++ b/core/src/mindustry/maps/filters/SpawnPathFilter.java @@ -28,7 +28,7 @@ public class SpawnPathFilter extends GenerateFilter{ @Override public char icon(){ - return Iconc.blockCommandCenter; + return Iconc.blockCoreZone; } @Override From 182ee7bffe4df14399a4e0b55d92a7d227c2da1d Mon Sep 17 00:00:00 2001 From: Dyx175 <77151433+Dyx175@users.noreply.github.com> Date: Sat, 27 Apr 2024 01:15:39 +1100 Subject: [PATCH 163/348] Update servers_v7.json (#9783) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 73fc7afa98..b77023d35e 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -94,7 +94,7 @@ }, { "name": "KMWStudios", - "address": ["mind.kmwstudios.xyz:28101", "mind.kmwstudios.xyz:28102", "mind.kmwstudios.xyz:28103", "mind.kmwstudios.xyz:28104", "mind.kmwstudios.xyz:28105", "mind.kmwstudios.xyz:28106", "mind.kmwstudios.xyz:28107"] + "address": ["kislota.kmwstudios.org:28101", "kislota.kmwstudios.org:28102", "kislota.kmwstudios.org:28103", "kislota.kmwstudios.org:28104", "kislota.kmwstudios.org:28105", "kislota.kmwstudios.org:28106", "kislota.kmwstudios.org:28107"] }, { "name": "XCore", From 5113f8770fc3bffea92ea858e806fcbf9bc6f8e6 Mon Sep 17 00:00:00 2001 From: summoner001 Date: Sun, 28 Apr 2024 00:42:00 +0200 Subject: [PATCH 164/348] Update bundle_hu.properties (#9786) Following the english bundle updates: Logic Code Loop --- core/assets/bundles/bundle_hu.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index 69d119e5a4..8c521530d5 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -590,7 +590,7 @@ filter.clear = Törlés filter.option.ignore = Elutasítás filter.scatter = Szétszórás filter.terrain = Domborzat -filter.logic = Logic +filter.logic = Logika filter.option.scale = Méretezés filter.option.chance = Gyakoriság @@ -614,8 +614,8 @@ filter.option.floor2 = Másodlagos talaj filter.option.threshold2 = Másodlagos küszöbérték filter.option.radius = Sugár filter.option.percentile = Százalék -filter.option.code = Code -filter.option.loop = Loop +filter.option.code = Kód +filter.option.loop = Hurok locales.info = Itt adhatsz hozzá különböző nyelvi csomagokat a pályádhoz. A nyelvi csomagokban minden tulajdonságnak van egy neve és egy értéke. Ezeket a tulajdonságokat a világprocesszorok és a célkitűzések is használhatják a saját neveikkel. Támogatják a szövegformázást (a helyőrzőket a tényleges értékükkel helyettesítik).\n\n[cyan]Példa tulajdonság:\n[]name: [accent]időzítő[]\nvalue: [accent]Példa időzítő, hátralévő idő: {0}[]\n\n[cyan]Használat:\n[]Beállítás célkitűzés szövegeként: [accent]@időzítő\n\n[]Írd be egy világprocesszorba:\n[accent]localeprint "időzítő"\nformat time\n[gray](ahol az idő egy külön számított változó) locales.deletelocale = Biztos, hogy törölni akarod ezt a nyelvi csomagot? From 302a6d732c59fd9202bc3f3f29c5759e312834a8 Mon Sep 17 00:00:00 2001 From: Brandons404 <43156510+Brandons404@users.noreply.github.com> Date: Sat, 27 Apr 2024 20:52:05 -0500 Subject: [PATCH 165/348] update servers_v7 (#9789) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index b77023d35e..7e7d8eef5e 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -134,7 +134,7 @@ }, { "name": "Fish", - "address": ["162.248.101.95", "162.248.100.98", "162.248.100.133", "162.248.102.204", "37.187.73.180:7012"] + "address": ["162.248.101.95", "162.248.100.98", "162.248.100.133", "162.248.102.204", "162.248.101.53", "37.187.73.180:7012"] }, { "name": "OmniCorp Classic", From a8da0bfcbf5fc921f67446404126db90e409116c Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 28 Apr 2024 09:26:02 -0400 Subject: [PATCH 166/348] Re-added android module in IntelliJ --- settings.gradle | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/settings.gradle b/settings.gradle index 350dc81511..b74305dad2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,13 +4,6 @@ if(JavaVersion.current().ordinal() < JavaVersion.VERSION_16.ordinal()){ include 'desktop', 'core', 'server', 'ios', 'annotations', 'tools', 'tests' -def use = { ... names -> - for(String name : names){ - include(name) - project(name).projectDir = new File(settingsDir, "../${name.substring(1).replace(":", "/")}") - } -} - def hasSdk = System.getenv("ANDROID_HOME") != null if(new File(settingsDir, 'local.properties').exists()){ @@ -26,9 +19,7 @@ if(hasSdk){ //why? because IntelliJ chokes on the new version of the Android plugin //UPDATE: it no longer chokes on AGP with the latest version, but instead gives a completely different error. brilliant. - if(!System.getProperty("jna.tmpdir")?.contains("JetBrains")){ - include 'android' - } + include 'android' }else{ println("No Android SDK found. Skipping Android module.") } From 2b7117119ccf5fddbdf327fdb5f06eefb997eb34 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 28 Apr 2024 09:52:46 -0400 Subject: [PATCH 167/348] Load order fix --- core/src/mindustry/mod/Mods.java | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java index c31f648637..3ece15b1d9 100644 --- a/core/src/mindustry/mod/Mods.java +++ b/core/src/mindustry/mod/Mods.java @@ -728,8 +728,10 @@ public class Mods implements Loadable{ Seq runs = new Seq<>(); for(LoadedMod mod : orderedMods()){ - ObjectMap currentRun = new ObjectMap<>(); + Seq unorderedContent = new Seq<>(); + ObjectMap orderedContent = new ObjectMap<>(); String[] contentOrder = mod.meta.contentOrder; + ObjectSet orderSet = contentOrder == null ? null : ObjectSet.with(contentOrder); if(mod.root.child("content").exists()){ Fi contentRoot = mod.root.child("content"); @@ -738,30 +740,32 @@ public class Mods implements Loadable{ Fi folder = contentRoot.child(lower + (lower.endsWith("s") ? "" : "s")); if(folder.exists()){ for(Fi file : folder.findAll(f -> f.extension().equals("json") || f.extension().equals("hjson"))){ - if(contentOrder == null){ - runs.add(new LoadRun(type, file, mod)); + + //if this is part of the ordered content, put it aside to be dealt with later + if(orderSet != null && orderSet.contains(file.nameWithoutExtension())){ + orderedContent.put(file.nameWithoutExtension(), new LoadRun(type, file, mod)); }else{ - currentRun.put(file.nameWithoutExtension(), new LoadRun(type, file, mod)); + unorderedContent.add(new LoadRun(type, file, mod)); } } } } } - Seq left = currentRun.keys().toSeq(); + //ordered content will be loaded first, if it exists if(contentOrder != null){ - for (String contentName : contentOrder){ - LoadRun run = currentRun.get(contentName); + for(String contentName : contentOrder){ + LoadRun run = orderedContent.get(contentName); if(run != null){ runs.add(run); - left.remove(contentName); }else{ Log.warn("Cannot find content defined in contentOrder: @", contentName); } } } - runs.addAll(left.map(name -> currentRun.get(name)).sort()); + //unordered content is sorted alphabetically per mod + runs.addAll(unorderedContent.sort()); } for(LoadRun l : runs){ From 50dc064f0f64317d4bd9fe37ca9932be0e2c91ac Mon Sep 17 00:00:00 2001 From: Cragent <117364120+Baldur404@users.noreply.github.com> Date: Mon, 29 Apr 2024 18:18:23 +0800 Subject: [PATCH 168/348] Update servers_v7.json (#9794) add one server address --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 7e7d8eef5e..1568ce2fe9 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -190,7 +190,7 @@ }, { "name": "Anana", - "address": ["mdt.mdtleague.top"] + "address": ["mdt.mdtleague.top","mdt2.mdtleague.top"] }, { "name": "Vortex", From 0bc45d72a77b9b88626776d002d607d9bfab4c2d Mon Sep 17 00:00:00 2001 From: NealRead <160118416+NealRead@users.noreply.github.com> Date: Tue, 30 Apr 2024 03:48:54 +0700 Subject: [PATCH 169/348] Update servers_v7.json (#9796) * Update servers_v7.json * Update servers_v7.json --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 1568ce2fe9..f9b7234c11 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -291,7 +291,7 @@ }, { "name": "NPTS", - "address": ["81.94.150.170"] + "address": ["178.20.45.59"] }, { "name": "Mindus•VN", From 56388cbc79ce1359a90bb13cc66a9063bd7424dc Mon Sep 17 00:00:00 2001 From: NealRead <160118416+NealRead@users.noreply.github.com> Date: Tue, 30 Apr 2024 20:43:53 +0700 Subject: [PATCH 170/348] Update servers_v7.json (#9798) * Update servers_v7.json * Update servers_v7.json * Update servers_v7.json --- servers_v7.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/servers_v7.json b/servers_v7.json index f9b7234c11..4d2295d0bf 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -289,10 +289,6 @@ "name": "MineCore", "address": ["194.247.42.11:27792", "194.247.42.11:27977", "194.247.42.61:27989", "194.247.42.181:28514"] }, - { - "name": "NPTS", - "address": ["178.20.45.59"] - }, { "name": "Mindus•VN", "address": ["alpha.ateex.cloud:19172"] From 53996ad82c3ba78fe9e8e0e8b1f46e7501030c6b Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 1 May 2024 20:40:12 -0400 Subject: [PATCH 171/348] Possible ArrayIndexOutOfBounds fix --- core/src/mindustry/ai/ControlPathfinder.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/ai/ControlPathfinder.java b/core/src/mindustry/ai/ControlPathfinder.java index 01cfb849df..f5ad811409 100644 --- a/core/src/mindustry/ai/ControlPathfinder.java +++ b/core/src/mindustry/ai/ControlPathfinder.java @@ -413,6 +413,7 @@ public class ControlPathfinder implements Runnable{ Cluster[] dim2 = dim1[pathCost]; + //TODO: how can index out of bounds happen? || clusterIndex >= dim2.length if(dim2 == null) return null; return dim2[clusterIndex]; @@ -827,7 +828,7 @@ public class ControlPathfinder implements Runnable{ if(nextCx >= 0 && nextCy >= 0 && nextCx < cwidth && nextCy < cheight){ Cluster nextCluster = getCreateCluster(team, pathCost, nextCx, nextCy); int relativeDir = (dir + 2) % 4; - LongSeq outerCons = nextCluster.portalConnections[relativeDir] == null ? null : nextCluster.portalConnections[relativeDir][portal]; + LongSeq outerCons = nextCluster.portalConnections[relativeDir] == null || nextCluster.portalConnections[relativeDir].length <= portal ? null : nextCluster.portalConnections[relativeDir][portal]; if(outerCons != null){ checkEdges(request, team, pathCost, current, endNodeIndex, nextCx, nextCy, outerCons); } @@ -1075,6 +1076,10 @@ public class ControlPathfinder implements Runnable{ return getPathPosition(unit, destination, destination, out, noResultFound); } + public boolean getPathPosition(Unit unit, Vec2 destination, Vec2 out, @Nullable boolean[] noResultFound){ + return getPathPosition(unit, destination, destination, out, noResultFound); + } + public boolean getPathPosition(Unit unit, Vec2 destination, Vec2 mainDestination, Vec2 out, @Nullable boolean[] noResultFound){ int costId = unit.type.pathCostId; PathCost cost = idToCost(costId); From d9ea8beae2d0206e235f935e0aeded12b59d5a61 Mon Sep 17 00:00:00 2001 From: Uenhe <119563256+Uenhe@users.noreply.github.com> Date: Thu, 2 May 2024 21:41:59 +0800 Subject: [PATCH 172/348] Call onRemoved() in StatusEffect.java when an effect is removed (#9801) * Call onRemoved() in StatusEffect.java when an effect is removed * Call onRemoved() in StatusEffect.java when an effect is removed * cleanup * cleanup --- core/src/mindustry/entities/comp/StatusComp.java | 6 ++++++ core/src/mindustry/type/StatusEffect.java | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/core/src/mindustry/entities/comp/StatusComp.java b/core/src/mindustry/entities/comp/StatusComp.java index 56b7559455..4d3dd11dd0 100644 --- a/core/src/mindustry/entities/comp/StatusComp.java +++ b/core/src/mindustry/entities/comp/StatusComp.java @@ -73,6 +73,7 @@ abstract class StatusComp implements Posc, Flyingc{ } void clearStatuses(){ + statuses.each(e -> e.effect.onRemoved(self())); statuses.clear(); } @@ -80,6 +81,7 @@ abstract class StatusComp implements Posc, Flyingc{ void unapply(StatusEffect effect){ statuses.remove(e -> { if(e.effect == effect){ + e.effect.onRemoved(self()); Pools.free(e); return true; } @@ -189,6 +191,10 @@ abstract class StatusComp implements Posc, Flyingc{ entry.time = Math.max(entry.time - Time.delta, 0); if(entry.effect == null || (entry.time <= 0 && !entry.effect.permanent)){ + if(entry.effect != null){ + entry.effect.onRemoved(self()); + } + Pools.free(entry); index --; statuses.remove(index); diff --git a/core/src/mindustry/type/StatusEffect.java b/core/src/mindustry/type/StatusEffect.java index 4ffb7a6183..2e79ed00e7 100644 --- a/core/src/mindustry/type/StatusEffect.java +++ b/core/src/mindustry/type/StatusEffect.java @@ -142,6 +142,11 @@ public class StatusEffect extends UnlockableContent{ } } + /** Called when status effect is removed. */ + public void onRemoved(Unit unit){ + + } + protected void trans(StatusEffect effect, TransitionHandler handler){ transitions.put(effect, handler); } From 143db647102aa679ec19435ac22cb8737169e7ba Mon Sep 17 00:00:00 2001 From: Leo <86385005+Leo-MathGuy@users.noreply.github.com> Date: Fri, 3 May 2024 00:56:09 +0500 Subject: [PATCH 173/348] Updated the core landing sound and added a launch sound (#9802) * Added core launch sound and remade the land sound, * Updated the land.ogg to use Synth Pad 3 * Added saw * Updated --- core/assets/music/coreLaunch.ogg | Bin 0 -> 187349 bytes core/assets/music/land.ogg | Bin 231417 -> 343048 bytes core/src/mindustry/core/Renderer.java | 6 ++++++ .../world/blocks/storage/CoreBlock.java | 5 +++++ 4 files changed, 11 insertions(+) create mode 100644 core/assets/music/coreLaunch.ogg diff --git a/core/assets/music/coreLaunch.ogg b/core/assets/music/coreLaunch.ogg new file mode 100644 index 0000000000000000000000000000000000000000..b4a1b55a68864f4b96bfe015ac1709b14552b91c GIT binary patch literal 187349 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ℑe9O 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 z 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 Qx8gB(;jf?Vgwo8I0b@X@?f?J) literal 0 HcmV?d00001 diff --git a/core/assets/music/land.ogg b/core/assets/music/land.ogg index 4cfd914673dc31dc32a1589c78afb24c080c0f1b..ef81ccc90ace4e528713e651dcacde780ffcc422 100644 GIT binary patch literal 343048 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⁣ZA$Qbp|y9~7+%c} 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%0D~<~`+s~%SF5I1`zqnBJ{)26= z>%QFA?49#IpysN<87Yow_ek0wnZ;3`@Q8kzpiC^&Zm%gnYpKMzup69)~-RcW<Y#3YrYD- zo_*`y1fJHPFDh0izd8MJ!HQXoG3GlbY@fB~T;t+1dVB|Jrh9co9<+7zZe5>beg5d; zGnaq-5RBTvBKFTUro%C;x_ZvW+x^OpzSf1diBA`$yLx@PllD*l_S{W*OTN8Ic~}~5 zwphxeG%w+e?Al`osu;hve^CE#5p}C2;`2kb3E!I5JUb$Kc#rmZ*Ztp4|NOiBnaEGu zJ$27sy}K9ut+n4c;>YUjXt%70%|3j3d%{;aJp1zf`nga}PbP`kx35Rb8pd2v+jM=A z1|yeO;|`a%MXVp(ubz_FVYWr^-She{zV9cpm3%O}xNJ^;xg=9g{QvG7yu6||TN9Vu zzb3gQS~Shmem-~Kk?CI+A2RIk+yC;uMnUo0NDHA~nyQ~SbG#^hJI`+Q&bfzpcm5VH zi}k$4&-m#4_U-x?o-GjUwb?6S(rUzEV;i@n?G)n|#(6bAbx)M#w|%~)pM1Zw@IdOP zk}uPY8oqC>e7MJ(u|!gTG17oqm?2ti-)1zPC{WEh~8e6i`j(uNcl{aKQ%Qb)59l7wS#$M;lnYy1YoL+D% zy5~{Zj6EovYZn%iC=;_bqPzY`D-V{Pkkr{t2E+zjx>HnVg7y zZ^QFg;r9RfHL7aIyOylyy}SQE=b?_bZl2RZ4+hWQ99c8x)+D{#ulIi`_-1I>AF-k$ z#bnju#%ddv^-jqz{>82FRp(JXek1bdck$JQi{dnRKL#%=nQ$b^>&U$2AM8E|87ere z`}^k7-87ZbNjJ`QAO5|3LNzB-bngn|OC4GJ_S-2+*drDY-s%&NYQSavz z6ku`l>HWVJ?Dv%Kxx8m~G6@daxqZ6M(nBhZ$GqQ8j6d`G>)N9S_ZY1`b++ko@#Be; z%e3Zhy?R?+uDIoT>)k8A?HZB_Kf3Mk|3CSYHVgyH> zfq}un=~vTC<}>&w7`~DJ(-_tit;X2$ zJd`~;lq2APnTW=RIo1x}p0JgtGBDhDd_C0kndRQ*R(lQh_@7S4%U4cN*tx?YPfNjj zT2D#RHBQDA^B;u7u_a74JMnM3_6s4MAKWfa@4x=)_l!l3<-oLW6(Qq_Cm!dX$*!G~x{~u* z^gg>^&u`2;zesB!yA>NRIh~H*qVnmAVdne%{x8$!*D%^Lui9?czC+vIZbfUS zTi)iY^Uph5Ue~K!+Q-?nV2|R*gK00Ek1WVJuzklhGmbXV1tHrXFVkvi@N_i0XrKH2 zMQsA_v@aq;`<84J(T-oZg#9e@@BZywulL9NzcqK>vdpSoYmd55x0EpdUY*kON?%}o z{hn_}{r6m0{`aND>;HGMm7YF-|9}4N|K+=H#>!r-P@4bmf}v=t-(L~;{yFNe{#RW$ zE2}?y^Wp5gtshn6gR9wZJpA}d!2Y|^&m3R=M}IPBK6xh`ymM2MSG1XH>e=&?-gs4UreXBjvODl>d%)BiAbmsX}VK3(zAKAXP)Z5^Ogap^tm75e5-aVho60^AC zWBAu|-xjWJ`}1Z+pRxViV;3JiSSD_Ka8B5ii|2e6?z{O(a6hNj<#Rr1-&Eoco(^0d z#I)4Z_E5zbaQ!i z|8>@9f{RNzc8K~Yu43k7E10I1QQY9v_`Jd2K7j6c#WfHE=a3iZc|w%W+^9V02O8IHkg%FlT$`%{15dT&$e@Z|4sJh`MCSr9)=w|-h9^Dy-RU%AM>x1QJ=;3%(Go1 zFA}jhwMIiB`@CF`=^GXI+5;C@&Mi2sSYz5=am#3Z?aoQz22)Kq-n@LJ)GFGRboNi) z!dkjoxL=#K&R&k0+hvGPty{S}y(0;rHHGSAUjYees`W z>h?bMCwJAhC$Prx&YHN=eroEJbxVB;4I8!^FAI_Fk14OcTyHqt_fcSW_zHntQ=Elo zJ0xU#Pf?uim-T4Y?5S5He*ZXAcU!w-BmH&j_Gl3;xp^nA?qUEG~_MH zuhlBHRA@9w4qWqP|E3?JZ}@iJGU3?J+doU==l0v1tvA4x4s79xgEbEZwLEVU)4?d z_O$v>#>#o^-?A#UK7R16e%+t&X;Xv#e_s6J`ko8>mOiz5{d~R5$+~^1Q;NT=cg&w= zdqO?G$h&x>@#Xy&3X7MBe=M9jRr}}8Ms0?FJ7X@YPEz~2fM?;ksXJUQ^l(pk$SN_N zPdxkYrsIs&ea3n7*pHb$dj0>)tph&}u>9WfEN}9~>iart>h0^Fg}>ODur~T4qt49D zb6yxVh)w=rKJDx+M!$~lyPMPA$obxqHSyff8^C)15<^LY*cDNU4eOJ;bAQBs@Hdb; z!1#~t2Llhwhjuq6Ru)+<8N~9T1Q@$@80_3)48`@ z>POn1J7+KPZ8(!!^qGVC+Pu}PWDkl=ai8ST;k$xm_mf*!eS6K-jdXT8@3Uaw5)=BJ zF>m>=<)U9V{aw(rZ0h?{6IcI??viW}iTJPQoqa*%#nX;8c1_u;i)yBOtgKY5Yu|TQ z_+S0a{k2u6%LDE2-~O{rZ3XA5IK3%$rmP0BQ{w0Cvz&9-r%k0qz`?L4W?u7^Dcyd_33(wdx%}(oDv+N0d%_A3IX=m6hr7U{t zrE$j94Yo!r_dh?r=Iah&u8Zf&x@+yfsVE99eUo0jZ+hpFy#dzlJQq&dvQ=AARA?B6{7mE#NDV(ayLX8+TBd;99|AJcCgXxvzy8^5=u zE_UCyQrSq~%AbFGV9w$AYU};e2W{QofBIU!L;u#ckn*?x4UKCSg_ql$+IC%5{O8x1Th~vt zfBsWRH9cm@`KUU}%hhvhU)3%%`c{{>{&4eAQTCjb@vrw<be^5ty7UIMjQ@2WtL4A&Syb@txXg0efmdh?&y%&! zw=JFW=vK~>%9iXWYhxUY6ef!OVl!Z1kX^t~amsRcrzb)Fqkysr?* z=lPnyZI4b&^~2|aJg3Z-J1Fj&|MnR}ZJ$=M{)>ohnREJ%=DZUZbl-RDvCIC9`_3{J zUDdTQahn(}zCWs!5g&GbextyK{z*05r?Sp8`+pT}WT|?6P43jJWjDhsB9=GsKTEl@ zeskFUb>HOpHeUSJ_nz~2#HDiH`R7Y2R{nQ>=l$~JyJK~~?-p(~S~6AB?#e=KxyRXG z^Cgw8GHnk@{e5@lS~JEK!PWY%N`DPM9aG}t>)Ml?QWR>q_11droa^uOWgE(}DkuAO z1_>^m^S||`#_{tua}NE_aJjK1!N*6RH}~9px091^{8Z~}Nn7|L?EAdcCfho`Y_qrG zkxsK&zb1Cd(On1k`t7L;_WxrWZzuS(wsG6}WafC<3pKKEl-D;7;|+LzOZ;9 zb@`~Vxn9S&NcTyze;;~hv)7jCmYld$({hfDkBe)RI99HFp1dt|mq7v_JWO%J21WaD#?2(^_GN${$2Ng_fA38bw6`|cbsYr zF$s@j-uRMzQ^M|WyLYLRxWrdcW2)- zX60H__kt&9D$fV09g%$Sak5_xmli|a%$Og>l1CX1yeRU{Tf9g@`-E4k_=87zHFmQu z&vw>PtFV$N{H1OGx8APl-&(uI9nT}&BIcgl_W$SQcV+k39^~^Luy}S?UbZqhaRKuV z^NdV)Hp%D8x9+`9mp8bzvcX*1F7z1tVg4JNC5+bB@Z2a4+`?9x_+~Hfg72ptaszGR zmoAPn*uuCxtYOZEb@C~7JeC_+L>%};7)%&g6`nHn@J*PU%D`~PHe+f6|Cb&9md8%N zue~1_v%h<~|6|XJBYZ#4-gADv_ptP;L$@;iR6J~qUHWEr#_zx9E1vg#nr)anW6>1n zSgY%AKez~HG5l?Ozw_(%$QResceSQ)HcN#nn&H+R`y`;bfxOcYTu-inbun=Y}@jtx-seDihNcz#=6~VMoNWH!_0_{$|88zSQhw_Gr2AP~ z+fHQVS^lxVH+i9$GTRe2kB0oHlQl8c^)Iu&T=@4VvX4_}>dvH} zy1$QGF5^)<6XLEB~G?`oHSZyLjw{fw6r&X_vUd-EA&70G92g!Y7 zoz2lOc~v=2=;E(k5v-fvXy;Fv_U3PUz`hqDl2&FKVs@U6&1qTl#N{^K5U-fx^3neB z0qYBHq8+pLdkI}>vQ2%k^^5T}L!EO)U;iD+op@8~xZFP@8IiKj_b>Z7$K9JB+ZkZ} zSNYD>;5D3+s{U^~cm3VYc@I9Xi(hEzBd+76W?Ibl=dj(f(_S4XlXfQF;Xj>yKBeUG zrvACpYD#w}{r+bBHLaHYx9GlU`4S7Xq`1MM75Tdc}A^oma2*krVaQSaS3wet6GH61QT|7rO;=UbEAGe>>B z)e&od{mVU_{bk>}(|NPretkOiLRUEV1r^Zw|94f&(l#ghC2*{Fk~G-QT32I`z|e7_ z<*n7PHoZ1mhH28goYxijc{vhzb|@qUG_ZS|EM}R|wA;~ulOf(vc&2lZWRpVRN$fyze%JGQk)SCv^SGZlecteqVFE*@>W9d;Hxu8l z;8BTg&1bcYkoKEt;;@jpu`y!(wJZ5zyXM539af4p+2p@XRzL67=JVOzs-c0&Tk2Ym zDm}gQWOm{mv2gi+NwN(gvqNIP2+sa*suinu_eV=kptshdInukO3f`B`>s!CyF7DsM z*t`T&uD4a6y!RD;J-UvWVSV+{$ImJkwO;;wrgY!8kDCiPblYD@#eD33)?f7bSGn~p zfw{G7OWz8H#`(Pa{*xhRSMIZ)+|G^7Pi<`fwc0m@tw52{SJFFv3Bh9!5O`rX-u5-mqG zOS@01Tsd$5{yodhTfa`dQfg=2S$XdAyVKoo+ge_n>;Lz1`pNH-`q^XJ6RV;mdPYj+ zYx!(^nE!ETg}MbY{%!im!qVcH>*+js+O4LZ--jkR$xFVGjAXSIVwl0^?X*a5al*1S zjDiOu8u*{^O|WJ1IHkhSpqUz-<1{D4W!V=7sapFe;ToYm$^tf8&$H4bqb@nJ8OWX4 zACa)L?Di@)(P{rmOBTm?-nwqr{TpYhmyAkBu5e zv2`zwF4j)oepU3AqgQ%Qzn}cds3L{)<$k~a-zfa@Pbu_Wf7>>DV{7?sPlOzPM(sZ8 z{ad(hoyra-cC~L2Q&w#I62~3RU3>H8$)#;9GUd7Y^;@UyI#d_6uTAy+HS>KAf5oQn z`;!&$A@S|o-63ZhXZ^0o?3r`gV_Qt$cay0if<5Xc<(mzE$;SOS+8A={RnpfPF>QL5 zJ)a!|r)XDCKWabgqvT4_*_F3+_x<pu7~@76hDwcNzB{G^=Y@sHnv4!O!MId^*h zsZ(ZimZ&DJZ>nU_>5Jnz%+MEVd->(+;O#FRe8NhfJx)KoW^vj-!>?*$8=ke?PL$uFyvFu!&Bfn$ zEZ^-dtF_ttEm!MWTWs!U?o3{e3p|%+Zuq@T?;Y0{--~ZA|IayJ;Inn&ra9Vg>-V2) zb!YgM&n50@TJ!sB?P-tNnV(Pcy_J9U(L5r!uB|(X-%xTI&$}&W=XkU2DO)&8ZZo&0 z#yTCAPr1??ndGwWXt}eTeBlr#RE-f z=DmLO>3Y<}>#WtgO$9TKNmSm8Z1~1rkj)n5EWAc0K0sPkX2|Axx*#Ia6mY$ zC~Rf;yTC0Qr$rr{w`@~utF+~@hcDdKL%BN-tYLewO2FXEssn9GyKB~E%kJ{AE|B}<|9n_cUQ%}BW^x!?VRf?_p zp8112TfVeUUAxY#(_o1?n|IAwXY-m1*K)JZo!(RXtNiYV{P@1rOM*JD8gF0sWaq41 zy$L>x)b?83dmMXin|hp^_`Kx)r~FQF*M8PlF)w_v`tCNnd;a1jug#{!^Ra6l z3m5PEt;^3fHC*>g`4OKjA7aY~J zEPU>9Z0`)*j3fW^)TVzn|IaB=$RcIp)O}=M-5#kmvVYAAl!|NTZGR@|u`%ktwC(q* zUf#95^2fe5O?BwmdyK8@`>Rej#T-i}w|#AFF&e8X)-)b_z{%tA$x;0v14F3g(+Bfx zD+3&v7#KY7s_$1hdg#w)pYE_)>A;eN1zAn{J4@`X)?D1Ty`GKXulN*i`3aXbCfuLW z|Lc^QVz{Zno^{n%>_3K1wr|{X-Z6SFkI{Fr?OU&Hu+d6<(D(A!ruV1nQ#~zfK0RW8 zaA5<3g_sD1K00T_ivi*?OXrTtz*`ztg^T-^W|4`tSodWab1?|6=d?QuW#up51W(qcZ#-( zNTfLTi@bNP&DA-4{NZ&yb}f-sX9?Xc+dM_L%qe5~U##QjC6m2!(J#;bS5qapTT@r; zIK8_!mwn>APtkuhPOiz(2{NDQ<(MBVu-Ck%cS6CpjDMC+R=WD<^zYy3JNw#>N1t8% zUwK|xUY?zKVx@llu8mjs>KW_*d#siHuJCa3E)n~nysI&p|5Z{Se%kOm?RVstCr=gs z&V95ww$iBm*8P)PQ*8AfFS)R$|L?bBE}J~WOC0XseRehO-_2>WFKe@E?|c(x)~;YL zQt(tfZ2B5@KaVTcbw~eA`=_&A)1&;Dwxy>v zUfkF9t7(x?K&Z49!@ik!rh2m+t!Y?wp)=;BcaK;)Yv{wvy02Ty-|ut#U;ONUdin&p z$CbPO|NhEYklhwHuWRkg0Ro0wTd3WIHrNq0p9^Z_Qw>`h`@%eMMk7R2*=hq%Q zZoAO*SHr!v*=d{CaYqSx^{1ZS#94f9`V1yx8800{q8j5jYH|zUM&qeAu}ar#`@9=<-BJP?=1Vb{T|;b zFNU?3K5`{muKMYC`f|(F=OtGH6EZL6v51G6#Os~8xmV6~-z1;+M~}0&oLE)zGu*TD zV5>mWX``7FBWEk_^kv_^?eWbnk!OWhciip!#~hMF0Ft4GjpFjG`X9#yp(Tw z)RkWgw(rvtIn3uT{^eh;jobF-2D?7Ky5kovvi4>l>fdm>Hf+j5 ztGCx0Ro5nLJgrsguJ-Nq=GoR|mk%CDX!~?-q5tdZ;u||&d`ta0Lp-te^y@8R-%hPN zclVQ;hn7(5Vk@TR-u{mpm=C{-)v!EoxNphxmR&Rd#ed|nY@4Zco>6S3o z-03ipL2j4L32&C8F^q1rPl(C}@*iSfVNl~Dy!+dS(w^IOcK`QQJW%>yS^53<)AP0m z&oA#y`_E;RzTo2=ft1Y+Yjz#_r&x4c|2(hM0?9YOJtC@W?JsN!-jSLux&B`cw?RlC zvjnI4fyoC~JPZ8pXpPwo+%Ua8e@YVHinlId*vrsITU@L(j#?79Ju+k&DzTG|-b-{#=^RzHr~` zOCb%5jMnt7-aXUh*u=U$citQ9+jco?htTe-qjj+pcFJA0c6dAe#=rgBr#Ds|$yb}Y z-~(sM!>iJ3-e+FxHS_X$%dl!<<0WyFd?IJN(#Bu^r$@}K{w3zyZd#$Y zqt-p^ueN5LewuOJxz+9so4PLr-q*1S-)8o-;?38`8h1KFrAj&OCTd)Zkd)QC`#*S7 zl+V9I32*k?8timzw-zI@NO zbzCQpyX)C?*VQu3etqArFGk_zf+BO_|L32E#?6`k-s|k&UB27D?dO|iYq3%G3x|IT zx8x(UL{GVV?cQsjIzJmfDzDu!-Tz7Kzs2FIwKd1hKUbc$e|2+SWO|Lwqduu?)_v!~ ze_o$E+vM|xbt;dJn;zKHHe<&NM{dj4dy4gnH=fuLKF{n%{^f5s)%i;n?Kg|D>A#}= zD)610j$sbhrn+5E?<+d)*j>x7WU}7=YfGl=-aXF_7RN4DzIRUM{<-1Hbc*9S1xBNNnK3_=T`pP)6+V0GqgM26one#$ zLrqe~CoRLDmA(Hb-~Y8~maob0-K+0^I&PaNUmtJqEqkBqyxD!dTTAQu-~aw_PqyzE zr^Ee==d~a13M;UkUdd4LJTvJw`%U-%lN&A1-&^o&W3kBpzKb)9*MC^InDN1~3^vR~g;ov?dc$!z>&`K=G^WpPTkd9_{h%RG2erR!P(C*I&*302OBNb*+`NA4_tW z=E=7IF1wtbc6e<+bRplkSHSaMQ`qA6eM^!k0L zlV2}Yzk6#-T-@pHrG}El|9y0NU+sDQF6DTesmbi=pG8Xd&&fG)zIyG1{jWd&vR!oe z((lcmCY-r5GlMhlz^NIvaZ|rChnKScwD&Tbzy%x2If)p{(PUs?P(0JKUgZD(z5oCJ zSNi||+W-GI{{O%D|Nnovdzp8p+8*k5Ji;^o^jsU|EgEwh8EcsS7W9~Zzumu?{oUNW zs(YtC@9$@^|0mt^@NIT(-i4!!yH(}bGWgnm^PHc5t)lNb%S03D8Q(1qzetK*B%QNy zzO_!}%Z!}2lW)J~+qpg3Bi@~7$Mfc=@u52s80LgEGlm{8VqjqY!1&2Qm_bn7?8yY% zN|pyrDvS(Wigs&XJ5!;0Ij$)d!pL!Q=6q*uy zI?Ru!bJ}cnZ?{0hl6)!$Mk6f&t_h3!gzjKfNUUhvDZ(Z%&oUQk2 zli<%qzSrFMK1*8WIJ@*!i+$0*Hvbi=F+vm9hQ`kc+;e5U{jZ0ADoWRGmA)(Rf4aEj zo1Pnws^=8yGS`)`{Ml>Brm$sg?JJATv+w?hV41X7IC0(CO5v5YKGn6>JZ5>^S6-fr z&p5#>8}>WT-EZ4>feYt9Oxyda`iZ2!;#F6t%-F7W zUZ?(}_LFNfbbfwYfBzu6ZKYoQsX1SaHsvs-+P>@L=biZ1J?#7P%5^qBUcY?2#^}=J z!_FL`oEf_RUDC@of0dXuJ!IXb?c0u@WZK7i&+^X6+&j&+jY@6)PtL^(t<8))z$Aa- zQBhRw|LN{_H;!{UzrJ|&xYbRLZ@Yi|6#qNDKYQuD`=5?{T=xIuNr{MRhTw$aXyMm7 z&DUe)wU4MO9XL_G&wbO|i^ZqCjZHRZZ!O)IFz;+r@4?5%Z8kiRpS5Mja+O83=hHG4 z9iGMhVNT;*u`E-={;4ef-dz z_gQO%oyNLepHK5|%CnuGUH$CM!}Gti_eDJFc7t z#oAKyw{5w5EkfA6w=w35+vm8jUjcuoFMTR>RCDrz`MdVmxp^9S`Wj}}yw2sk)d^>~nccnak+*$r$*kJ;|AnvOw`=Q`@4ED-E<^8m?klU8C7Zv_ zG3OFX-ewwI_3qZuyW)1Y-mLD+zjl1-&x79oQu{J&+xAuYMaI7We|)O;b#Jx3zrSj) z|K)Mk`Nm)6CBNr>xAS~isr6}lewzsAD?Z1wj+1At{v@~GvmhhrM=7j z6L&fs%R5%PrY`F9nf*IIKl)g4=gmY1`+vgB?7u%* zHE-D*BP@NYKd}7tWVUCR((qoNNg{>ufLOBrgs?B%EL;maowl@RGN>_1bTAY#KR6l7 zmB!$3;!RBK{rj~q&z#>WTzw{DxnEY-&gm=4PDH1-IeuTZW`j}7v3HYr4m_J}dMsz< zM?RDKb#v$%Jer;Kv{j!=fMxf?1r zo~SF(4|6;$uw%Q~mc>{5oGTAm#XkIT=gT4P;_LgJ|KI=V{C)eD7umbJpES7m-rk>E zvo`1cqT|<`4eN!z88rXA^fN>El=uHCOXrZM2-efD7XwG%4h+nOal zo4WCQZWDgnZ(`~1@z(bH(w>j9FZV>m-sCm@@4_uPoB!M2GU-Vo_jvw?c%S|pdPZJZ zP4ea1(&>9;o<}V%`xUkOk;dB3dsgISUEiTr$7N~cZ17h8=b{(IIn$^A)IX83v|UI^ zJ+%Fa*d;xwkU)Rd-5>eW`z3Gi*W}k57^Ix3lPZYOI`%)P#BLX>#@YzK!}X4zd!s7n zAM5vjxVb*2KzH5$S3kaPwbt*O>a|g{)GFR*+pJwbYm#5owAKm8#e7|T|MT;tRPnN! z5X*`u`}S0vR{yvEuZ4H<)V=)@3Mc=)S?0f@zG(0HqL`D1FaI*yFUR7}xw_PT%4*T? zt^fbOJAbYDhVhFXM?T#Ve)I8;w8>I=f3wS%H*GwUAEUAP+TG%pAu*{li|#xBN`G!D z8?Eg9wL0%@`li129Vx4VKDa!3bL6!bTZp}nRWY~yJ&Bn&ul0Za`@KeJrP1r}9zIt~ zd+W<1_|u$z?SB#duU=css^UVr_YUZ`)R{c|D)^S1m8Y*=yHrr%i31 z`1qdteTHL#eG?29-Q4c`>TzYKoSybsgL~rd{xkCgG%Vo=(CXot(d(Vpyt{$lHDQL( zg$Il|)A=S&5L_d*n1L&Rq3D|vy%zk{-nJ;?AU{=w$glB58?U_EPWZ3f zcZ=_^lA@9DuVeN<_Si)7N(-)Q4&jgd-TmJ_?T+&Dy8HJkw=4afEV8A@rM&#)?t;kF zBb$zj>HeSmKlp(2oV_0&eLl2rhWelPw~zN;|77rP&twnFRk5MEfzc1Q7*V^hIHAAZCw&!AK&fVw5fjM#e!nL z#j|7o4Bg*V z^lsmOgR*BozIO<$R{N96zwy7>v)e{LcjR5Yb$hqF@}_I6?QP9vmBLv+Jzo2NV`$UO zqrOL?DLW@j-XnFwtvQwB=dIAgF?Wt7 zvW6J`oFBTvOS6HAVV~ulpUPs(V;F*;7KN;zXtJ1DqE?OVxBTC$>%IS5K4zX3;okB+ z|89N#ukXq2+rM)C^4RWq{=&pL+55k|KePS@cT7|zt98x&wHtdKf21Vt(bxR=TH=|k z*TMb++sfW)n=HJoXTTuQ+SmG%kzs%;8$7HGz#m zp_HLKT#b>zrDW2dqh&=)GY%<;-+%t+`@H%ak!>?U4 zul#fUzw`VgkNrE>+$rDjzU-Rd;xY#1)2j^&+{#Y`oHMVi%?x{ew9u(RzlC9+Tz>FE z-)k(p?ri1ijd^PyzHK#kQjFfyd;hg}TbO-1-@4l2lkU+eo1#Db-+BAhqfgK8xVJr1 zoV(7a_v7?_7T32&E-yA$IHEUqmB%_g`RT_5=X_kTrnf;S?09{cPKWznop!gv8uh&% zkAg1T__px+xyDO1$vbPlCS6!vTvQzVFMhtu?a-Ok?BBRne$t5!dEw)cICY|}Z~vtP z!!;?^_qXc@e`vXO_F>@N8}s+RmsqRujlDJ7s4g*Vf7(xPo-cuNDc!Bvg<7WD@2`6$ zr+n$Pj?k3$r$?sloLF{XvcT_in^*32k#6hvL>A-1GlyeIw@doeA-YhT6AZd*A<_t!5wc zKYR0qVgrkPQnJVWrqpp>_@Lx1&;d-u0iRh>HO z#G3!VbFcPfTdCcwUnDAPKWO=%jxVxVA=gy(Un3*c{*m8)ox^SKlF#Lo+!vnZ_;<>r zr1_#3GwL3^csli@n2p=%ht@*s`?5~Q#>x5~(_JOI$ZzldPi1!*7~V73Y3AHf-6WZN zv0&B=>zVDf-WkDq0o?LI^Y{K=`PXn}-w(gT^CE(eS3dr8yFS(+@_fT=a+cH}Jb}3)P|5HYL+Vg+L*Z7Sy{{0ZFNO)*yHF@g8b?@)(`(|#D zz2;}$jOU8?!(!aDU%uXOTxwHjqmE=|%f`K%*xS!mZ9V>ao#%%B>8jEJa`R26O}MdH zcH?o~^fi0Sp4{U;d9E}z;ax;?%hm-&f<-yM`o2n)Esl;gkyL&(EkLtpxrx9Bc~b_1 zsV>Z7pwl1M-7#h0KX}$~GQ)EEBN zw+?C78ZB9z*tR+N{!-(&OZ1+8?@3zsz`Z;3`%CBTyytg3`~P(JjrJurS3b4*t&zL^ z=GK;9XF4yZN}Ja|t*;bguMSyqTVn5pd$;0W)q6+gb6T0bU-n*mnuuk3Z_f|b_qV<+ zdUW^s`{Wm8nt}WNYb>nZsS^^L%e`*zYo*V3t|Z*Gd!IC=)bY3RjM=kVq&+1b-Ll?$ z@)-u2@(*!WUXyEsw%(dyNHvClnLD0bI=&U~$Q@&A*nN1mJgt^QVWVNcTW zlRMr?KXLt($WpUu@*hXr1Br%vo8GGeJppsGVxP# zNK@HNR$HZ>xz#Csi~q@-w2Y{1l*_2!;(NkTGx`2=i_1OXGW}ECYSfP3Ib$=s@X9TB zPTW9*ezdq@Ak3@^ciI%w>5+1(}8`SDE{66D+|4EPd&#$%N=X!VU&fRHb zY;fiwVsJ)_fq|j_Pj@+VaE2Q)M#J$Rw3TVczQ~x9xjcT+jLZC=bLs`o`o^gBK}qqv zpZ>+8=|&vYvT64B|E=e=S?BSAdw%!)WS*ANxZk(#L}%Y&`ZE7H)2~|}J{LclnR4LO z`m0j<3GZL!^7dVQF>j-F@3W`*Wp{TRyJzs~Guyln1BL|#3z!ZxaMdpeVqg<`#aP{` z?JfLeg6m1m?_SVOX&#)l<^vLHO}ybJpEha3OB112^yN zj^H!f*Bx45Z;<*&PqE>)snAjmhCkkawJW5R-cI#AJ!|!q0=DjBV&{dNr!ah}vcA;J zxbyBIXOr~H%Fi|lGp)ZEFQt>A>m}s#Apg>@Zjncw_8-FDrXTfpV{s0)lggc2yt1ys zD^2hC(^K)wOEw41%h-@n^Zow%;Fgj}EWc6?m7O!lD#@E39k+5~b$H3|>5)#^pFc|( zuM^oS!T#&S&55@RPsizseO*7pCgs7u>3Puy?JaE0A6(h+cgLpxGqXkFQxCLg7;?VJ z_uyv#X`grc>QSdRF5$Va7T=UGocG0}`$DS0lu6l!d!<*`i!Yq|v1?oWkJvYIVY@Z+ zwnUaLpO$;?laqGs*U;Wi`_lq?zI`g5@jP^EPDefa5=)yGHB;|w(v)}8*R^Ed`ue%x z#ojZ%D);xjNvnGLKksRr@A`e3*N&GtEt$K0k}&6``HKowu1UO`yli){du-B`;Be`k zVs}=T9DT|a;(RFcf4lAHy*E~UlK*|IR%is0#`S(rtb?swO z6Pv{q_s*TIIllNClV>K!L+aJXmOU6p$F>eo+gKBxYQuB<-65D>US#7}wF z7qMEY2P?i`yL5iV{AFL2nxA;pOr2-=*XLPGd|2~F{lCiZ)BVmK(0+ZMB{J)5CHsf+ z?}f73)f1-5IOkkbSjiMzt0%Wyrn0K)%lE(P|GQ6_h;WKLV7^yr)8YMLQHg>!!}K#V zceKrD@jmpjO*~=F+w;c~de1sYUG50}b5%HnWzXN{!hX?ehwc8A9f_9qdeV8n3*L!F zWUqN!6#ZU1a`8#W-CtL4Xgv8uomqYQUOnYok`nXv=PrAmv3`HT@y`X5%(tl?zo2Z9 zx`~T-!K8Nvjp;rNY;OenrGMS4HtF6_Fkkl1rFm!HobO7LOXSzFUmSUWTU>&F&;7tu zLpH{&1O^5+6UJ7K1p$mJ8AI3^c;q+_O!Z`7*t9~{zg>RrqK&L`et4dGv~bggSC4|& z=KPNk5A9En@SC{HkumSK?V?i;I-aXJJ-fHgu*802;`xO4Wx;>!d$ZXtu{_}O`f0fB zb;p8=DgK&=v)=CedMU70GpEaF!`7Eq{5dy9{#zu~bX0rWB)g0E_<#QNQp$>)GX0zL zryp%vf#NnQ@op^eoz>&*^ z~td+TD&Guz_N)wj-@X?ym{zAsF>GX9)$ zpHZdaayn=A>JXVy@f@x6u>UT;idli{{3TDos*K{O1-4g+*d0$73u`v{@8k)Wv9}! zJfEu5SDXG!s9h+w;nJnMe+7IFEPBzE)O_st{)f(AmChb2+0nU8*mJu0`Hg`e-uYj< zq~NN=r^35(@qXr4o!h^wZ1eix>3rtS|0sXPpjDxlck?bOJN5V9EXAGWyVd#%7M?o) zS^v;)AwdE=sRR@ zwQF7a^VPAnhimO-?W@aHU$e`hxM1%5y*uZYZ7z7c=lYB0tKv7--&Q@SS{`wcNk5nM zLr?rZ;WO#{=Gp!F){3+D)=50x%G;s*sn}X!n%(7OfqMq^Njw!M4!w>STsHf@kaK9f z+qFl9F{5ufU%f~J-=8NO2@DK8*Cww{U|?8t_F5TJxRR&%rQbX|uX?oq+nA*>&>g%UI z{e%e*6HaAr-tgZ)j{nBNIbm<7*O;$q3-$GUds9cdt;Oygn?X-_^0lYd9u-k+N74^1 z$>7|qnta+PsB?a^idX90T|u+TELPMRYD)nA2*-7?q1G^r!B>wGs9~d4b$I0 zn*aHa#H7l=t6qO+vw7e6y#7P<-AP~6UtHVo5_(K&O8ombm-~%p9{Fv2^Y#C#P)TeCIn%8>a;mm#i%}ai?BkTdqU?|uk;f?{KaOs^{!?M8>ju3WjH?lesAB%_Ku0Eu5-@G>`h@j3^{t? zg}LUnGi6U)Xy^Cce0=Nl|NCF1j%r zcHd?W{wg5X)cD|hgXV+Pj?I0I9r7A1CwLt^cpWzRFfj;h-SThi6`OEBUHytT0eSV0 zQ%^(`e|}Jy<(wF>{W5n-q=RBYJ<+^;WLy;zb zf0rjPF4o(XEF5ILYYwkvj`pFnT-%&A)<60V6nFCZbvIw%_4_pUqob2vWX3KsXIXuw zZePTIZua*(StHMV%ND zmme`+cthYU$g2S+wvTZ+F5&}cgsZcxz)Y;Z(aK_ zT>WvAuq4mUx&yN~A0%zn_?MfWy&!Nlw0;EXx}4t`kO2 zcT@*P4PVF(dX3kE!*t{~Y!(i&x1%zP~#D)5ouAvGG3E$A7cfy?J`R^8IV3_tl2FH@8cF zpB$XA;KPbv^1JuVvHfbdIQQrC?A3dW?-j9227A2YJ7v*w=4l$^!+rl<-WC61_YH~m z(^F7?C|H>9`*C?_+ocm?2ae1UiV){s!Emqg&d%%=i%OZin5-tM?{Nv*!XLmLZ}2ee zPt403|I7ab?5o6M&N(NI`|%F<1=)3zgsuCzd4BUu<51f;OLF_X z)yC_}gb&E|6+AnZ`tAGk6yuhRr0*Ty>oR!_j2KK_Zi{9r`+t)m`kUB)rl{#o#_l&R z-ndp}|Ng8)X7t`0>W9M_67(iG%=mPG*>uD6dll*NbI&bf6=`VXR^ee{ILpZJK_I9s zRfwVCl<%|F;_vg*e^2Y$SX{HXe|A-}ZqfdOjbBzC+IjYQEMN8<)~|O23wobNg|sxv z9-g-(Qr7p(ULL_`MM0n4#daI%FyvqQ|Jah{gjHAk`PKO^Gv{jYtnuz%_h{a-nt6{I zws`w12r;jzx+mp-Jf627jJi9{J^z8j}3+kg8fAXCAZd>vdH>H!ZpN z#q^{{r-YQf#H+(OE;shA;hy~Rl99Z`PQ|Wk60^igH{Mc{_9=~EwGY02tA5_UUOWKo1Yz-|Ihm-zwHi5GrxRBeeKU@zMO33nYruonPW|-enrI9 z9QBAh;Gx~P#b)1+gqusYoZ_BTK2dM;x8<>C+FXK40~g%P?z>Rf{LK31meW1q$uSR#pPr(Zs)*F8aXAE`O$`mA##qdB@L8V-K1`ERv4mEi>@w+oL zl~?Fq`S*2Km1BDKwT~=Hmx6C?sDFFK;%W51=p76@tnMklunwJkMQvi7Qs)YXIg{7! zy%7@0z-KFEc3{P)!neZ5%rAq(27uOx7Pqy%1DSP;Y zZAnp{`yAa%eyg9Yj_1&+x->WJT~ek%@hjDT;?)a-B&9=QSDx>FBd>mMg~yw`P6w&5 zp19C?NlH&|e%pEJ;yo+AIUghcofHhxH2lo&cJg*X+4F?opS5q!ejAyU@P6L(?@eoU zmw3*yo6!I1a5wWy<8a+6yH>w`v$mu0%gyX1AMMLGW?eIlQL~+POj~kSSB2W-PK&<< zTbE8W`M&6_2gg>8yDqQxTV*|y`6adQvC@uD!pCE|-=F?4H+}BuTK}5;)z8n%KfiD^ z`|kMwv-gKS|2}v1+}<4bnd#Zr<&yiji{)=WeD>7aef8(ZtM??^hqKi_zoT_&mtF1e z_mW=nk2(14*B89nG2`#erVae_;!2+_{l~pF$Xnj-Td2>3b7y6~o}PGO=Xt%g;g8jw z=Lr@~clUU8@XL+uH~4+;Uw=|>^`h^K!iA^joR8cu4vn6(yx(bCNBD(38p4WhO7$VS z&toUN*kQGV;}Em+hdu$$xi>%kf4g~Q$IQ*IqGVQOc3$0d;_Z5?bn$M-$8smH{fXWD zY==>V1CtSSBhydB_=+|I1A|OJ@OscezpVfN|7ZXIe;<*TIQaZs5r~^-@U3gh2P>5O<&Kymyl5G*CE^H{kpSzzHDZG^~?y}5Hr@ZKIQMO zM#u7*KH!Yvp8vaB?Zw6Id$%YE-uT@2Y5G*pdY%hj6P`PHcwV}|urlC)2EzdchhDx5 z4D;UAGKL8-Fa$iQyDYwJ_Xa)9#MMn_!*}(ZimdFq+b7A|z4xb-bU4F@d54VCnA3L$ zI~{scaiGNN&i=i1bCPv-%3fP^Rpp|qp6G(8dr7A_SC~DG-q=6A>{-IQ@^@W(kMA{` zux0nDrvGV&XZiB@eoKA$ZDFg^x(ze)55K-<@N7$T;W@+BgSTR4|NTADZMr{K&cmoL z*S>i_y4LAdv*mSd?0tvre|E=Kvwk;UT7Lf9hkeeU9`7mDu+ufTYqu`Dqw#L+%M1C_ zrkwF__q978+4ZYCPf9+qzPk4O@gp1a-_PBdwKj2Xw3&a?*@prT!_^k9G`Bf0y?FZd zY!B|8e=Y9m_gRFeMZ}a|G)&;%zp83U-qwL||khaPTb6?cX{wTSq zFEPsO=j!8pPohG-k_F8pt3T^-+z#kZTKIn2^c-2Po6n}b*Zq~txXI5~-}CJE{C!h6 znvNFPte^G4O60|xgup+OG=3>?KUh*KVRxsPEh4PZ;a`@ka9pAI5|f!;$}$S4g1sG1 zBny}Zu3C{>X5^;YQ7rp1KYF+72a|XM!MRe;PrJ=r$RlH_<+-G=aQl-xPd+_*CAFVl zYHIeh*S1wV_t{@t|EvFV<7ug%KSclD`Yd;CLBW!rArq#BeK@lG*YwP5g`M{v-~V3A z7jo+D^y%yR_88bdG$?+5C-UiuaIH@U_jLu=-dG&4<+}d~Tia@--LKo8Oq^4^=Wx|t z=1mLhuR3)9t>|3-NKq~KpG?Clh7XK(nK3)Or!0$QsFBitVy4!$b5(@#FP%5erxX@TU<2v(s$UB2= z0R!6#CP#J+*#hnccTooqPXlHKgItlV%0l{Yz_0xJ|!;%<}mef#{ybHG1+%^jC{$JYI!^AK>^rQHNeLth7>jd?E zO8#8De%}6oZ@VKJUM)GL5cMEs+lPPuvv->9t>J%fs`=CO!aLJ7tG_SfedCs%B`9Vy zZ|U*nM}(OY?^Ztfv$?yieoy>2#%W^bzleK&+jGB8$oZG0t@NSCn@TDYq)Y$*yrRsJ z^6Tr{rDFHK>$ZNes92M5G0ImpN$sfTuEj2~xjx`AE>{2av80}7^PX=NYlZwj96jYH8z23Rf5+_l`wLT3o_<+k?k2vjzTnfrlR7Ui z)hy)`ySe*qY1)fSj)yD%MQh)mc&leu-lE4ZOD$`1gBC_?*Vne4D);ZRAg|%6sF~M0 zw-_mY^KKWkmzViJ-SUpa4(@7qRsLPG{Xe}ivH0d4AAkJPvn%tyu!e7lDVV?L^@}M+ zI*+&gGitmZlsVypdg033lEu8!ZN3{-?QnTo@pn6OhvND-Ykd}E6;CmFnd*CeZ{zn1 zG3E;VwZ%S-7Z0z$`DpEd%bw~pb^o7t5_jDB)b08IO^g~5*P5P%hHm*{`8UOUZ%T*p z{O`M0eU1)4e%GdNW?@A5M}>#ZZaH_WXMDD;e$~Hv_m>x3yvF_4=WaM$^Dy(p&!;v! zcq)IrcaIGhjrDnNS|sLwH-GP^he1=Nch2tauDZElwS?x~#Y+}j{QaQ4#nL*i^u(13 zMa&Z?EMYQYm{)n{XYM33>qemxt?hrzCM{!lAnfDwJpQ}Ief`TvCeG=&obl*a|JFn! zYd#74cU!+!>?=Dq`FK{(=8SJFZvQ+AF7y~xU%d9wYu;-tV3E7JiZ@{H>IG-a z7i|n}f5i0rar(}xFY{NHZF;zDVbA7Gf3p|s9?0g=4ZmjkZKBX))wOqu?)cT-o*R+P z{Pb+Kgn9O@sQ92Ks`)#o>;8^jUoiEZZMps)>!%k2_I=)QRPWmUZ%buQOr7`pobl;q z+jqA#y_lAsDcOIol|^RpHRcOV7GXgaL40i{Vxf&mArG(S_RiU`&4i*Q1h-= z)~4C3ja9a&9q#)5|9f<%%b{-qPfta7E;#G!z_rv@t7d=yLe0r7JdP)tf0#SnQ@4M) z`9qLQ(?zTDlUshp6gnzJcTH?@Pnm$j$uSD8%o zl`TBJTkqQQ+Sysp*M3>F;ko474<}bYyK-wSZ|$e}oWJklE0nCSKFg3ec>kx#`CZz* z<&L{k&#)Wb6z5AfB z>TaNo_3GETr+cz4%KsLhFgg9tsiJ)|?PvU+w&UqvpWoaSMN^MIo|YH#d*A+l>?^Gn z?ymmcxJtdDcH)F9_NM0!i!6V(L-TMcW1QriAKoe6LYxmS?a;ct!z@=dkazyo$r=Au z?bqMdKbz$CzWKuam-asY{lB;TE%vYY?`ypLg`vr{D{tTI*WG@mKi%ZZJ*&8b_ms=d z*!#U+(VhLx_RRacJ=e1qn44`W*rXSKFS-BY&Zmafn@ZWHot`h49QoI?xTa)}PEmu- zt%Lpt`?8P8Ub_^%;m!38<`Zr&JUm}!3!9#Sm_flQk%|S%3z+`}J2NVrWoNj?aKLpQ zH^aOdhL{EQ33P5&3$K49p)^-z~v z@2A>E-|q*#?oZCxyJG8#B8y`QxdO^2|6T6CNcKLkFz#Z(5xEch<(8X79bZ5H{y(O( z_o6&bhV9C@TJicwamkNQf9y{Nmeyr>`A#hV6ZYOEEZiyP!m2#=%NeS#c}t9Rbie1l zjdDMHaJ!%=-|}VqzuABMmvpFJ-%$Rd)D+e*+>8O?~%8E zk$vr%WBl#8vG3*l^HaPUvsm`byT3X3ePTL?^u9eTy6%szn;ppb8M{BAl1!aBO#aAbj?@Tj?D4w8v%r z`Ti?-eywWa4U9hIx=fV8f0fJkb;l?AD1~+(dU$@>*7ZwVK5IL2TsPJU41C^fE%aL9 z*2$ZznqO^oc22sth2Pior+8i8x(Q+b{vBHW>+VrI_LtwvE1h@my4*C|>S&Mt-P}7{ zoZMWxg0r>Urig#`&pUSY_Op3^{>RpQoBLn=tCRMPy-XYj?fcxP>0g>>8XTRS|7K=w z&i%(Let9|X|0$9@Z_U~~+bu7?ncizo{cn6jJNvL&t+e_bgTDsR@>{m7TWS3H_^F3C zzOLMxYB~J?tDI-d6yr9fH4B)gA2Hpr#71&;!>yK#iirk%`y{S^JHe@*l&Zb>|E~XX z34hKr$CPbX=Ifg%T&?u~vy+m}i`{ih9#a2z7)Yw|nla2QoVVa<`+S|>yX;!M8=Q~H zDjkul6^lsjTqnQo_pH_Y1rqIMGqgh=Oj_@7>VlQ%go(dz8NT0m{wNE>pOevXzoUHR zBg6zNG=o`%9*0=zJYxzF=31wae%^9v3M&J{vlaEOMRL9#-xLj6&+7~_ z>wo`?nZhVi^XtCb>HmREa)+N}pRxKk*XR}Nvc2N#gTpI!*2ljqpBKB} zF?&{aN%XaeVM4Q}YO zAAF1Yd%9cJ?(c`NkJEYkYPR_P%uzqOwPsUYpt07r%U@32F57&f<+bi!$;p4e`D<*~ z{yg=1W6bRNwc9i1_8*J6*;4aKMn9SF$CjDm?dN=LzSoCMpHe)%Rs8&)J9jT`o^X5n zrRMCNFAwS;iT@iJw5{z@`oroa%Y3>Q=cI06{-vW)ZNQVI#QZOFkHGYZu5C?OFKqPw z=zd|6jQ*jLvahyme)e30m)Gu1yxGJ5|0LV%uR9Xf)ZONN9wD*jef0j=@O??@Yu@lP z2#4+c!N|LzXl_FH)?a-))}PJ#bNgyT?%Lb063+F`V!yibp!S2t%4<>%!ZLq<^LQJZ z_Z;OqVDv6oM5DF$X=Zd2i-Bpv!OT)dPj9AaeR_6;mZnn2PH%hWk5_uA7ba>@P@%`>0NpDtEU-_G@C;FgMKlbWs{>54rOmhFF z1uuN}w<)drk!U2}{5Uk{^a8=|M?2PaJ^n7-@{>_pENJ$f<=wfwKK_YQPX(8iMy0rZ zTaco^nwTuI`G7&0z~xjjB+2-byLN5L?pu1|eovjpzDuji{{JoimH7Xyl0${xw9jv^+y47C z`Ky~;*yp{LZzU_|IcW7dMrJp^y}4iS?~d1TS8KM-39UTS%eUj>+>LKm9iCQi9vAp) zV(}9Ba{g@Fk7tk1I(=R}KkvHxF6%FKo^L)L+fn+z@8tfplZAeQy+6;ZFWn(?>VN%{ zH|4+Qy9rakLSCV>`&X$}u9BRf*VjC^;D*zA z#%qDc{`1WIwavhHYif)dgR)8jBXkr;kc9yRZcC@!)nQ;@_^-0!hv@(RyAfA4?fC!y zKga+7Kk71Nc4lYzMmm`0$cY#5UR}b$7y85GM)Kd)7WY0I)>h8Cax!v9*N?>iGx>J^ z-Ng5r|F7cg#DqhV*9Cun{nf+j_NRmGtdhxh`Q@e;#1Cp$^h(COv)>ngu%57aC}F)N!FPsjLwQ25q)#G(QsHx>ECH*mA#c+P%-(EcoH(6OX;U|LZnS5C2ztsBAFUBq#s)G4&yy|l-8t5uTG^UGJR2X4+)Ik>@7^xfgCWm7MID%RU!dACPFKb+x( z)BoAbE|>ninHF{Wc+%xZ!3FcXEN}DdeVaG+$CHNA>t8}EH=X>u+1Mld7F$He&SOn$ zX7+f@>X~@yaayn5?k^m(bDmDfak&>g)pXyzTx;d#@`Ei4R!3Sb=H{KFeY7=Y`b=NP zqpK$@|K(rZ^&~ega$C=F=M{_{pJ(m9CAm!X^A>-;O%IhGFU@_zw@DyH%|6|BCyS;+uc%u=^V+{>Xj%`IKp& z`=?*${-^$L#UXJaz{T(>-*Pl&NM^2pF)9?N2&cBee zH|NiMW8U#PVGr^1?>t3O39{ktGi%&kaLYUgU~h4($WZlvwGGMC%nYs?pU zXOX8KdjGd;PteNUdoKS+E`xo}ZBc{MTlig87`}e1#B9mEc=_7OLYA;+*2+h%&o%h^ zn=IV_`_{d~T~n=j+$GKAJZ0W`?-75?w6#aA*Lpv%$x~*LgYIl=kK6xG{(iFl$lAGX zZ>DUy^7rtQ_`vrUtPJF(T_lpfWwIOoIL_;l;3AvD!XCb@vfQoW@7tSNnT7T~yp8UCChIaCfbx$%7U5A2_%s9p76zlehR>0PFdqGuRsr9%0}LxyabY z$Z(I*scd@_3xmt1;A=K(pRdZizL^eY4Gw6U!Bh ztFrJqL&HU8S-zSDw6|q?+;fUxwZ$qX)c)zH_|) z`{>Py(2aYog+Ff1T9d|}6?OXX{iz>k-O+u*x3=)ZtM7lDEXZ!QKn zAD^`N<ZNw zKIQ&uOGJ*y`?Z?Vf>#zgpWC+}MP}=JEw=RKcSOVUlykTK?2SJi?lfh(r1@!H$p!ZH zcYk#q`*;3{V8?}dTK9MV*u200&2*0KRTuQu$L-#~M}CRizCFg<^yLcQ-4UHrRJr%_ z9*bYWdrQyv?f*ZsU%daxi53f++)S2-`~hCdwe6=r)&GYs!1=OW!*p4r#<8LxAwO8Ed}rI z+42}y^gn-hI`rK3QxQpG_62K-*M5!5Ui|*;He>gaH#dG7moLb?aq#XkgQ?3H8CBZ& z8F;u)%GVo<{P1Eu!*EXe2Gb1m9ch*CbzX!V<67*TwqyQ+b7t=Qt2CYy zV!R<_D90KQc>ky4^i3^G_TA&NT_eBgz5ka6sXgtlAC-G(uQ~WOXz%Ht&v#c%a9>kZ zQ=TwQw4VL^(SHF+c}L6^W_^&YJ$*e*^z#45>kDH3?)`R6Ebgbz3Trusb$NaNH*Z@z zXW9R5369dr2>){xZst#Vm=177zv0#`{#3W#@6MUawXMmb*KF@}$8L$aUU(z%qyDr_ z_v)(yt|Z58Sr9KNwb=1%RpNW6g?AWtOggf!)y-r56}ydxc73Rux;k#%)vI>ZO6@-n zp3b;0S$@-^=;W>|Q$% zQk2y7$!2!yPwrLE>b?D<6WQL0t%_G)n4)=aj}t4Ci$ISwU;6rg=c{L3&~g51^ncbY zDOLH4`*;?6x$fHM!?UkqX-58+_iHRoe{Qb-^z+#ZshW93M;GVu3n%ZHxB36fp0lz0 zYxe$%U108iwZ#0jS8~tSEwjAsV~Q?`&wumgg5JrSeK&XAU2bpcee?UbwoA=QUj=^s z)z5o5>-)W}^Wv&1O_$27@9&%NIq&R&)Jws8kL`1Ataw#BF)L;IejQQadBXQtl)_H-4>v%A5~!mb0%lXIDL( zJWKBN=hXk@Yqqc7((umz{>-Dl<7-%bl5Cix)|$@q`RZCMzWnRdt7&WbCF=DVFR6b0 zW9=*OU}BDBoLl{~&5FAwPfyswXfuDshVQRxRU+0gbMf3bDtSS30i%S+9|qkAr}A9& zw9kH$J9B}3{XB(kE`cIH&B{3}%nS`*i+g$h&YITmcT}z~M)+B(Rp0{+y_=II>|SQS zex$?lDOBQ*S$pH@z#CaR1%%e?J$fy3eInOQ|?DU2zhB#$Ma@%K58buRPbOS1*2d-NF2e40rxD{OWcUyu2v1 z>W7bG&(-}KgI*~-T=_jx??3Bgm&;EMoIaC&P&xC-*7NzbYUbEh$hkN1O zFJT5pqyvvkt&ewqQ`DBc?4RlM$vf{Z}=x2wA*&8u8*#HC&vzbBhI4$UWPg>g2Qsz7Jye78q%;jG-W#s|(y2_Xfw##O1640;~_MCH~ zTF7$+Lyg0qf7k0jZux)y#(w@|E}M_fzn*OKTYhtk-Cb$DjlZ)F)!lwEXHG$__|XW) zd27R~9eOfSj%Z3h6YO0*b?w&c!W|YnRimcXE8FWOCzfxDyx}Xq?l@amROW%|$NlxJ zJ=eEB`Nwc1+vLXS!Z)k;n{Hy)eE#E!T;tX+hU*y{wYEI@-~(u`HKu*9ouFa&%aAzL2Ho>Zh`A z2Jw^2Tz-h%bmdQuoX%iVTA`f%7s_fwsNF>=HadUIm5{B!SuCLWG!O(oVxd) zjr)Fmit?tXt8Tozo8Y1EfBx^Lt2{h}=&M9$9PJTfmM zw8`etOe6Pi*#%hv--81BJr>{6JD>5-rLw#I+62}h93TYB$)x?Vu*`i(EE4|lUBta5t!y3u@X zx4G)9%{zk5o0jrVopOWiXkK+QFN0myo`&iz=2{vD(?9l|dy?eI@I&ax^YHB4WgS~WIkeR zF84+B&FQ2=KXx9OaesAY{KFF8`r|xyXFG0ooVC6tQ7x_8;oxz$>hg2(fv+%F}oIr3SK%|FNdPK4!0BoBjMYa}B>Q657O%LRrOrVa%O z28OhQhv%mzzb|c!`stv2%Cxp@v2o!gUG0yPmG^${+R1Qwqs!La&sJ*PQPXv>`czPv zH)EZPhu~|=;ytWpjdLG4`Az?K`3sAK-tW|JQ-i7-*FCOqmM-6M{bld$f66)=u3dhZ zm=vFQP3nf~$*sTIPIm8KC7d0&V%m4B5BB#`7R;}Gwg39}Z7*KdEpYzy+54VXhSL{g zVLxtXb=v!9z7zbt#yab}m8;hz_u zPRv?e{^(?#eVfz5qek4{CcjwuEhKM!i<;^c>9D;!lF|wp_y4S4=4kihQloy#S4N{} z*H+)wo}E2;?gt}>m#=UBSh{$@W^WY>_xZ`^e@r}4a_wqPm93o2k2kCI)<3PgbWs2O zvGOgBK0!;iEzy{^SGb00)yCq!0H&*xoF4oa?O4Pp^)b;@Y{`|Do+B}l6ECi86Z_CL zea|Od3D52SZ7*KEdUny}-L8#1?9+-4|M;^*pWj_>`uEuOy0d>Z^OSPAxu*VoY2df~ zWtzF|zx?Q5#&^OV-`)C5bozILtwzs-&-;|ro-Zz*efr>$z2{edkNE6Ux54;a|Lfw= zc+TM5mqD8gZpyBTTKE0g1pmEn-tPN<8gMDOKrdV$yfK2qV63$zyuwb(L@Z) z=rS-c@Y<;!5c&WA;Q#-B{{R0k02!hY{QrM@+{`&st65I3QAln)`RH8yq={$b62364 z+5Ox9VP|2%=fpOLuvfr-!`N#y;pG#BYG#TdXeW0shzbQrM=8uB7lh3yoA7FKOuGA)bmM!4jqiw!V z8;WmD%x&M7Yjf{>*w;nEe@Z?3TgwidR(=-cwtnsPYa3_xp5uOP@bsAY{FRn-n@?Y= z&)Yjy!i!hozS8-OdErrZtNu52PUCs1C#L4CdgpJ&6gv}jWkJ#Yi9OHUg&7`QD%0EP z?JRawsrG}w&QFs%8zeH)ZcGsqGk)i193=EndyQ`FL@gOj#ePNOq7AG!w;ngW%{%GA z>sRxw&usmd%PtgJbgj=(z!20Qm-0hHD)?Y~stWKK}P;+k^MZPG60EHQnC%w_R*Z zw8h@A7|-+{H2)u&7XCM{a&ph?r2Q_}_V7>mczLh$xgRh7`j))&@at93U*FqN_G{+HuH(CJ zcycqG*Ncny-nO7RtJ~54dPC!v^~>6)zDzT2&^*By(kH(@Rk3+{7W2GJo0HK!W{;Uf zPl(9mp8lQoqN6f zwW)Gaen#B>s*(e8e+91nog)3Z|E$EuxW|_2GI7_kEDpbux8B@xHUIV38&QAf?roGk z_PeOLMQ+Wi`~%+~bDdDupAeAGU9;xqEz3Q}<2<cw;)>npk1^Yl`#Wt!whl1TzR0Ps(6v7T(U%U?3#7=)Hq(p7}hpM=uiK@|CUw1_V4od z|J}lUE-#eMcTx>7KO6s5^p*eN_4#umquY<}W>S9S{qDZYsXa5gi_UaSGxC{ywc}N& zjm*K*4Gk8}yi@i0D^A5L9=>&a<4mSsw~j{iZH>O^wt7unU+3)wSL^@({djEh*MskO z3P}F1c3rvVlsB7Yal-s{wsl*}I}#<;WKG}ZA3dRXQtp1*nrN0NxipEN+h1&Yzd!ln z&63-{_g{b0v|^6$zQmmzGcLL=l>W8w$M@{m=_l`M&ODmbxnFBSz9*A^`0btE|3l1| zP5S%hp{MEp4*x|n+~YfUC0~#6ZRPJgwUPNnM&mIjo(6fD2i^ugz7+k0-+TkR*?XI{S(Z*?g3$;B>y|7o|~HNX7<*P7^?d;gbTWRH%Uv#aQq>x-QiOM}1XS_q^MAalJ&+1Mk~sgwOpfwqnivvoB)4%)glWcmLq$SdyQbTE!0P1XUv>X)ZoJMevE6&A^OCP;_Fju$;4XD5yFHry zlZ<;t<$LV{Y3)aES2PRPJ~?`(io5%?)S8RMd&)2O7`L$XecxADv!+R zjrpEfUbJ?e zWk5J!RGj4sl=w@khu*jnD#B%u!x2l@6J`}ic-46d^ zclm$9$$a+mcMG3wmMQo#A+IK46T>{Yn!fzq7YmnV%s>6vU2K-r|H`S$dEUEi|8_3A z?uGp4F7JHtj+r_CWACv2oayN6!^0V!^|3N`hQQwk^0o6{uYYUkP=7qQNco+Z*$(9| zLi?`gs+k_tYTA^f_|mR+a;O@E@vhB$iyOZ!RPpWqx-@Q&eXGK!((T&&bvjPl`&CGs zR$$r^P;ca{jvb&8Xh)`}2MoQ{i9Q^%K;;W~cr?#-_UeV8q|_O}P&lww%vCsdhPg zzvdR_4XTyb`6DF+4yT`R2~4n=b*umMos#RJ%W{IxB>p*Cw`g6&hIW~yZr^HF`>C%K zJN3Eyi)+fFyY-GQ`z=%RNVO`)dh?;Gtxv!GcQgFiprdAk3}`fAa*-(MTAvX0d*~L$vPu#9LweFhkm%200T#^C=7BSu^{8D$nl5b0PT`uFb6;eL@&)R+2{=6&R zdA;aiZp(Cq2kXs0`o3SjPp<8wR|D%y&$Y8|GH-vopl_R` zFC6#}GbQjeq(|`BePH1-*^|Z4a3#>0@r31$-MdA77#IRPA1-_#92KhTxFrA8lJ_pU zn?2f;CZE{v`ak9)$6`~954ZL&-y(2g#=9F!PwRd!ul%&XpXE8P`eoYC{5<_}RmA^oT3-~F{? z+0mEV4E7hRv0S&aeRTYfO3#|Cn{U1q2kxj9i|+N=p0bwz_w?eLM4c6Ow>A5&e^xnn zyUH-a`v2nK`rp-Gg!)(i-gkNWzVm#~{#{_NOMKdSW9PY`o5|B&_E@y;aB^=KlNSnGjH|W!me1QYr6lIxV;toa8>Z9;j91u+7<-7SNdYHB9EtBwB0N7 z{O794zuy0z^>VYce)un|f9i@?pTFpJ>skJ4uV8Mx+^gMM>%-Ti&;JvBCth~x%GfDd zp}&sKxVG_~!pfjDHO0^zhJPM&u4em0U*d`q%iz)bV-}>6$tt=2L(`PvU%m!bXLmbV z{rEI5e*eGE%qH?bq$M7|%zV3-t?JkD#lj^CHE%~MUr1fWdw#%%Be;fx7 zyX@eeU_ER1)%Llc1<%fOwbq~U`&M%8qcUP#4xWl9bDZ1x6;n<#FmT-7 zQnfhm{~l-UE9J8m9Gg7#T6xLiZw+D6tVWqmuXcrMaZff&h_Oywx4$tsD1F_ej^jM% z`IUG+K3V8-ZqJEVSMNSCoynTOJ%6cu&19Fdo4W09Nag&W z0)6HBt*0km4xKn5{^z^;L{sg1V!!LH=eJt>DKJ0lbw&HCC^uT9PqCFdid!xkE6Jkq?ii%)Q7Lp{Z=_y z=CWb=+J|mkuIZodr*{^9jM4ru&riE4M1S9YedRgERj)E<9pChSv4Y1o!D+LvKYuLx z`&jI||2|7L`4(-oJh$)3?a)^*|IEs1oHym`yqN z4QjGgIOxOwqPTr6zw6u^2R|>GpIo)+!JoR4wg0Cr+W7C}%vGtkxauvf{nEPq7aMQ& zfBsM;erL(V<)z=Ai(dUb^>>eYWL3h+?&~jOt;2QJ^;JZdt-n3n(0W^OZurL?&ny3B z?OgBToGmGECf>RCMwQjopWhhs-`egqn^F7b#q+N1ch;}|cPy>0wtH{ayjLHugiQMF zvbjv>ntA;EuWReemtX5GIJ4{b;e98!Rr*9M_r7EOgPEx`%&z!8zlik3{Y`TJLe%d# z&)m7E=H~j|J@YGr6Q=9@t>|Wnuc9I*nd!n!Ht3EglsF74EB_b+3|nyxZ$*%!gSK-T5!%qz`U8 zH1|Hc^k(KcHk-4Uzg6_VeZP7A$4d_k+5^r@E|~qayT>YmNk3r!wa*6U49=f9al2lV z_rSf><0=nYRFD6;Kfk`P|6GT>clu9(|EImLTg6{x?ACPut{J{rB-5;EO;Je2iv?RZ z|3A(hW>~(_bH$Hx*JHEh%ny0h<}V%I7rnpeS`I@&UuNG)p&w1kn*xqIzSvRe@u6zN z^$d6Bw9RUQQHE0cr3_cQTsrgY&D9lq{mouH3*Rbrq^I)jxm_X8#i~^~b}3{$cv$Cn zllAp7#`Vt*dPY^x+qK|W?aF}MNW)A;%iVdo@_XGLdHh@#w)SJ(y0fd}^3@}*p51;R z{rS4CGM>xV7Vj5*_oHjyx95$1-A8A9epR>oTK~y6Td!+ywqO1KroEaYJ9T%h;rI9I zB4N?nM8*8Bs@@Nsw@B~j&AH3jO~0If{?5NT`ndPHTN?8Bq&KU4*>vgOQ_J@Ai~mI4 ze13w3FZa`alUEBi?vdTS@$bJq>erV7qG9mx8?GmPg4)iJHQ4zFUAou;G)OCz+hg-kR<&7|8L0hm*@ZgZ~y=Q z)&Ku5?pe&)ncXA)mg(w-NSl+M~02x?6bcKC;PoMlYMZ*{M^0{k+A<~Cf?1G*Lzv9 z`-ZgKl@XMEb-b3x;j zYxk_@c|DpVCtPr1eVvY8U(=y^UslO~x9Bl4pEM&s{`J&5MH6|ya~n%t`my37YeRaZyqMOH>+Na6v5}J44 zub?JJyYiwepR4SpgR^w6x3T{{z_x!IUpq@iVflt#7q6A*kavOdz1DOd{&4f>l{x%t;uCZE=bT9jiN7!_)YUli#L0i&smmlk zJ+gnc#^{0K?4YX0{nsMb1%!**s%5Y1u=*SH`OcKdbx%He$*%S;t^8Ul=Q00<;eUCr zoq69BN`rICeVw+(o_pN0$7^fczrFWY9R7S;ztT2#<;*S0>$m>U`u(QXc&^A%xl_MS zdwQN$+whO2!l5jgyH-2M=zYzZ;~Sr+~Hc2hAkWU&i;L``)^NMhEZ|$g5BRaw>*-JiF~&8_3h=^HBssB zcd(tXlzyuw8Ps$8-J65&*-8@jur1M&JIkb@%e`S~;*k?IH;dkQf7#fSwv$2MP~hK& z6Awikq)G}TJs5iI88lcIFqdRaa4JY;VAvz;`o8ATon!v*CC{B({&>>YrBW01#QyuV z{Ft%!#;??XtguyI<}r%9m-V~9t(cTMr|70+>!+<8g&)H0&pctaSh|}!w=gW@;`Te4G7PV`?R&6Z)fT-MPQ&UEcl%>8fPRVv2TSKH+8UfN~<^~^g-`#;>0 zT^Hu8cu<<}tChEc-{;(``@i2f3f4yKzkT}6wDqZlU%veC->jSWdG$BJ^H22K1W!h; zEdG>leDB2E+Pxber^d_ti!=YZa{csa`Kjl9jdJyDPo00B|2y&Nf|T`Tf9mu%WYpcS zm#B(5X5{&*EY^)qukTsE?wVnh>GWNvlBX7*2u$7oCGK9Q`LpiZ z5B|+`dpT|Q=~Z3Pe~h|scLzStvVZ<$*Gpr;C0l=gnt6-*uifVMsA9v3+s20zlNh-e>N0H(+BPkXRd~!h^+f1i_id?64_Hsk{^Jt=SYV>l zxBQo%e(mz#us_qGcGuCp-UbNpRR6npH=!XUWjWvn_>s|Ob zoZ~()w27(wXl?u%Q1Ft0)kO2h0_KtiMg|YLJ2M#>E`-=Q_O5vMXYHli=Z+>=|N1rY zyrq7pT7vb-@a+7DrF!S2W*y~Cn#KFa?|w2v*Mep0^;u`6m!A9=Q5osEWv1exeG`A1 zEl8+Zz39WGW}AF-)6WJkDU(>Hbk}+RDqGyY?~PA? zcIeu_uP=Oy`?F?PC*J$gHC1={@2=as)-RrI{xxLptuK+Mr_Klz5c<5e>iv5IgDrDY zdjoDBU;HFmqV9_+cnNgyMC{a>vrFv@iFet z+)-b-0gn;4eH2TQp-KP=x$TH*5fNLWrHK{ zyS6TpIg#Q!dm4+E@ajp89`Bd6epJ>xo#`sK>0yM^TaF*|Z(NT&WvsdR<5{`4$@gRC z-#*WJSz!NJ^V^TsX0QITTGqT`{(PzJ&TPN7?NBOo3i-IBaN*~5PhPnEe6;fY!ur{Z zYp;DY4QyVt`gqgMmm60cuep|Q9=vwbk6AWGHy=-J*MEMqwSMcp^X~8OElQvAzwG~t zT@O0b{g~qt2A?JxM=TQBcZidU=Vr+O*0>r3%JR z>%JCUw{)4gr2qews`5Yi?i(H{y!d*}?W7( zd$g@-IcLgIQ|{w2d=0kd3;r~3jq*RLVe-QG+f5G5t%iGp-X{ioPGpS8ezQ-kCSNZ; zH&D@St6K7Z-#P31)?csrEO_vj)DFLk)9klMC2c%@A!XvD{mvmOUXX=d+Z{9Pg(dRbCr*ML$n=Q*e@pn3Y(h(9ysC#`?MvM}i%AAO5J-@SB#T_}zSM zar~Lh6|ZXk9rEu=F21uk*z#|EY^MI_>0MtL{Pe2(qwpwl6N5IM4lnpI-bUk9R-!D)s6c@hG^JI-YRha*1O;<@sTH zUEQX?o~h^dmj14J>ie|&`>oFp!UgW-&Ae=Se{FnA$K505Z!d;U_iS3o)8)*pKF9y* z+T)+~Y)#%I&Ei(cug&K#o|pgbl%VbNiSuvFcr74l z`R8!S`^{Ht1r48S*3EE_Kd!a6*zU`+(z^o#Ij{Y;^0mOBnO~$e3GBkK3eK8Jv6BYX5Kw zwFkWSUoYrhImQ2f=WnyR=E>oE4nMY;c56itR|##f0P-lp=uzJAx8|JiJB{zs@=)V;oTV=;fq_G^!A*KItaeL%HOYC-0W z$yL{~rdxB)w36w$`grrFth`x&_q;hX@%J^A8vE^D=BXUmBJ^jq0T^ z&P*1D1G>`o%hIl?v_GHlsdnRC#rpO4e+TJ?1uu2@{`_(Kgr$$zefS!uH3i8>B+PB? zUVd;@o|bb@$a$T|$!#llt+dSb<$hfMD(uhxOxODPJ+?>s?ZfaRlQ~#3oF*owII=Tgm7Ws0AQ>91;xw>wtp_TaEg$}?}i@4uKY8nzuweq-8wXiERtL5 zoF(cJ;c00rV>8`3d3IrZ?t2FVqwO_z-LCR#6-C=0%=xL-{U_tiB*#VJaS|J@J6_6u zczs*Efv};hTdZEn=^GCAZ{v*bCET96Gur%SXF*E6O7H&VuiakNT)&}nb9pgOt43sG z``UfaydJ1*|Muu0$D{LR0bG)%&#uh;*SbLRz~9cD9P=~TUY`oO#Qpbu`j5w-N|yg9 zpZDI(Vo&YT%G~ea9NYbF74KEqWb7JoCG~LquhgKP-$!RNaXru|T&w!=w!wL${QuGS zzPU~}ZZW@HELJgpVN7aZU)}ZjbuU_Mzm@aP=dE)n{QNdi(q;bfl6kMqWE*n2zNXn( zIY_@>5$^x{edQOfSLx2z{(X7%?1R9zZ--tf+W24dUa8OZB{*65cFxJ||KG9fUQqj5 z_v`W>;wwKcX;%L7UMq0XL7g_MuYv!T8ZWVuG`D@=-O05t#a)3|*Z-H8XCDt^{k*FOJW&HnqbKjYW_{Ox%*^O84DD*pX9d(GVA7Yw(* zW9Pp0=IZsGpI7(JyYygZaqr#@Q9qMoHui11C^h}>@9k+lXJjjt6c3!;%%5O#aOd;~ z+v67=yjHz`-QAT3=UJE4SiY)0^MUw!5NP^` zi9xA>?*b#2!>2c(=~9EtrTz8y-iXha{iR%2E4P2eI(>aH=?Rw7iHwrZE$1G2x8ZWe zYAMa8rl{Agp9KPRa*f}lUH6?~v`Xch;Zl~rcmAd+tz61%9kC(7B4z#C&+*G%R4DpC zGws)s%edcDvR*pq)YPi+0$ey=Tk zx9#{B*?szVK#EuY*NpK0CwD%R>vgv(y0dFXcwOOvGZ!!PN~gKyzx(#$sai$qbpK^@ zB`-bs_P=LeK&;{0S*dnASwH`*JvncswK4A{)^+Cdr<+90RbTsJLBRBwm<<<~HvXJp zQxiSyi)u#Lk51iRs!FpC9=go4I=3|Z>wOE45GmYJb zlk7ZcUIm*yv(&zw-nVJR&+ivxTRShjih9N8*yXd-D>V46^@*TLC)@lSYgp z>TLUQChOA*nR)j=tFJ$v_o4LX$3VsEKWV+i8jp@{-mdgeJOH*kDH1UzqtC#=uvwJb zLHPgwPyhda2H&srpZWj)_y7OjniqNJP<7i36)m-?#~z=#Yy5@ND(4}qMfAs*Zzk_z zZ_Kg%AHVzh{ePe5T~L5H^uL=9 zeB64k^YWTw9rm?%7Vcbgu=ZWApxD`&PtP+RJ5_k2|NH%`y>iuxXI|S&h}35a`FnKU z-sQZo`&vY-!=1&f^Ew;;-f2tyz;_^;Y4d!p1})tOCv!}HRUJEJC*8&_@Pa{?p||bn zWygn1=T8Q2XkuYtXi~bgog+;)c*n<0mFgaIedjuetdcD9#6R0-s9`$^Esc}-@0S#waU6Ut%UEFKK(QN zbTlRL$i=!Ff~`z(U)&G>(oo+Vx2|lGrHroJyN{O+*J&)<5h3`acJJ0(j4Sq4U(HQc zjG5G)e=0s#^?$g#ewhA0LH%ygk{SMWHFNr%za$l{bM@d;aa#JTJVNYXA4j#XuzEB5 z%elg-zfJqRZt7k1U|aBH{r&p>sXV_*eD;=p{a|4J-+cZ?5h=E>f1=hM+kWT5_xU{2 z?{WLCU$$q7U^h!z)047Il^Lb$7r$OnzeZl-(69O1qu#!$Z+_PMVs?FceMn8IOL$ha zZ|gJn9s9GTHopJ#=lpi>Ypnm)WR>oJ7{9Xf!#93~?YEn+woH6_d;4T*3-b%pe_Yqy z;LUyKv+0FUWgoY*e%>2}%)d{MUb?(I%5F;KFUg(HSK1fXxf=6#ca9*-UgOqd z``?DXS~#)IZOca;z9s*zuHWv}{;`+a*7x-xz3N{p?dG{p5DM>pBfImLQ`M<19sR9W zpR-Dz{;alcUEe#yS9el4*8KmoV&L_5_cXmcjUU%=C@%#waz>3RPU-tA%GR(8wBe-2++BYUq4;{Z# z%93Xz7b2cwNd3aW8IvD`~PhlWLMVi zk-hy_;_cks*Nhhbe>>^fl4ITBee-9XxL6;j|I@5*?@R8#VGXq+wT-&-m=7Fa6{^;G zf7gLo@m(x)#}%e<=lcitrmy2w{H9uUnCmF};=+BGZtVQ<_O9ZziP!eB3z$i5v|pQe zg6+23g4~)R>HuqH-m-2p!VLy*^;Z4Uj93GRVsg~-;A4=)z*m>drg!L z&B$i03(gUjZnM~?CDi#{mf@UBp>TWtE9T5i0oITAe!nE5e=}LRNPo)K4NSaTre`^e zH{QGT=tqp7yvsl7<@FQpfB*0+$iVE*|9LxK?EfiWJ?%>G>%aBP^&&-bjcVaaF){yM z9i8Ya&ey!?Kx66TJ<#H~@^|@cpt$6j~r2M&*wTtJz4HWK9-u9He=JL*u^~d%m@4e>s zKCS4-|M{P%h;RDy{=gI^+^@;zZ8b#{<*DYnY-dj>Vb>h+WUl+9G z?G*YJu=(SnlSh>wZ`ybb^lt6lqaRU zx!f5iRa+x)vA*ToghJcGmR*8*R=@8ZW4gs3zI1QQ|A3n^X{>2h9JRGKcioeGTX64r ztjX*r+oW7-|7UQDQc+p+8V z$(V+RTmSz%a}kZj+z?yj=Cd?7y{-)`mWg z=h)I>7!nrbK5#nBtipfKp_fA;T3dpFAt=)E(W|pLQE&LZJlCH6ev4DroJUehdoKS7 zvDlgairvCYxFV%u^Mfne+!Gi3KHhS%*Nj*D?a6oTO{((S4e}-YzSuATqnS zxxe3vcOQ=3`0%*={okdMVgAYP_s?>3&)C0l1ABDRj@=$D%JY6K-aP5X!>U@Ft0~+k zuT*WanHHJytT1o;vADJK&c(C2i>E03a{Kl5`aeJRj$`kC$`-|3u!*>}>)zSIZCW*X zcUz~New?3Scr2iQk-=8qSIgP1&p*4S=KMmGQSY|F&~HYLgne7C)|DyL12Z3BomB9%p>J zSKlsv*2Qdc;VQN{Tla3wz1MI`Q0s{hWy?ky*8Px-a$fJJi9hV&Gp<2&nZuL{M5 z#74!RbDk(sS0=ZUNB8*F9jP6HlS9;HVrE86&f0CPRI}D#!Ows3)6}($ou0HT3EEcu zYGaMR?Dg3;v98&H=k70!d~SSw`qX*9EbjfYUh-dU{pMSb&hWqNewbBwMV#+rt>w+- zZ~Xu5U-$dW!P85&&A;B)y}rTEX{FiE%N?uDKk3HDr@3p@T&dWjG{bY{D!-V(a6a9M z>#nTar?c}y_S6%;k$V?ZN2Ju+v!1Jt*|q-J;a8H3TpSW#KCkC@E0|YurjGBip3KJl zi+r2D@@aRPHel)`t8 zH|Y(mHYUk@8C(AS-P6oIxzo^6*XK+6$DM08ep`A~BIAAM`s9j#5np_s*%h7XG)-en z%=~`U;FtExEo>J~-OZdZGcTAizVKq*#*J$({{GC`6nE(r`;pSU2HCqrYx}vL913|k zAxzGJx0}y^fjNvh!k@QccY+YZf)_55_bY$;OncV0Tg2+~udbb1#(tLTN=^4n`DAur z#rC2VRhevmd>-6VSI>|A!s6DvKklLOQzc8M$a72bR=IpvJjtL}xmEL;!GSsLb+HEnmsT$p}8zJ)DtsjN7=7kzmiGt#iF+o%*)txn}aG$17JK>Due#n!jtlf6(u)belBkNbYUZ1;4kL2iOMb z|GO)^Wcii+=b`PL{#D&F`#-HoxuJF_{$2O2SN|4lm>Bzcb4VXUiQGxKQ_@dcHmo-I zldZ_4JL%HR8sE3~r)Nx4VUz|nVir}i&F$eiu8)4tC*dYzz4svTyJI>)3l!+3bv|Ww9sw z#-BEm0~6Mtu%5`naGiCdWSIB$Q_l8f&kr0dUU8;1Q-OC9!vfLQH$J?XWc*s)GFwu^ z#_ar*@cQ$+?>1{JR=6G(*R?(Bp$;Zvmd|ES)aRl)}O`M|JN=)BDZ3k2OGfX!xRdKPqeq6CS`4Zog69qHQhL^YTzI~N{+D`QMpZ!K1 z{FVDR3foU?%9>iTsHNJp#QjX($Yr~m6S#keO zuhski>^r&lLM(65{=X}%)Lmzsx_|fosqIXy>nER${Fj;%VRGDMy5!gMQ|q($@@5I< z6nyEL_IRnp<{VZw$=fG>ml&*?ppqOXDY@dxtY>z$dY5zMu7$jEx_$KV_w(OM1H0N1 zZ)x$~x%)V7WAt-oPp#0y9`X~pE_sRV>2fSyyTRV>;L9m?a{INvge%=izq3xmzEb{@ z^p@!8wjJ3umZHxE^tEm$p9;Nl^!QU-yE&3)?+c%e+r4m$+OqP38;^d)oO!LY`q9=f z*{{CyuGH7GN=4Lc(W^aMsy}U-;kkcLdREU3eWSN@&DQX&FV|;RB(9oR^GVkKgnG@2 zsf(F|mc_?!DtTHBDgPmr~$@HA~TU=TJKX3o6-lLit$h28o zKH}r?U(ff|1?YF^+lU;5q7%kRvj7K6qxrp4kPukbq6$4}RO&wXH#D?{bp`&Mh* z7V{V|FJeq+F3XSOwKRX2rgy68uAV|KlZ1kJ0~<4|4JWGtQ%5s*5(C4ExT!iO9oHq3 zpP$y*vflcTm9uiRo_X(`xU-KEHh)h#sJNm#>HuSh?8k|l7Y3f5V_ki7e`Ct}{`tqh z`7fDVcSe!7q4x9AIn$F5{_6EFVm>Y{TJD^y7}!38|G%Y7Q=Mro2b;_8Lpzjy@l^if z`*(QWa_+aG*W7)>&je2SpZVQ(m6iTq5mTPj{C)0A9Ti>+A5NU;wq?&v^=oPUM%{Jy zzn)&K6tDKe=*hx!`f^*|&9C#%l@;q=6mlU_4R=l(_ zZ<{~`v!Z2}__}{PzN&qbW@ml<`zW`vwBGAN_v*-JhF3QGzMA;+k=d{M?whktHAp?^ zzq9`wQ}a>l7t4Lwb7jK+wurM-}zg&Ot zU=Wkf#J4gV=Pp_D({0tKY2xc6qJyMAtcd-$zOzuP)>bqlL2Rv2X{w8O)UFxqv2Eph z&qNev{e8lBvG;Y#^U!|H#mCL>rtN#aU%A$z^4Syfck632`mg=F({%eoUG2TM>z10? z=AB6|3%Qx!JGZdo`gNV^+xtU9uSKoBo%$ku+JIPUmaV9m_ZU zaCm>~Q*C<9rRlb=Y8QR#QtXzP{&1dp^!$PK_4%CZ7+0VFRLSsYPut?eOzAtv%g<}s zL~B^;#NXJqW9s@Ro0U(euV-%C^Xk}by-CF)J6;^EJ$k2T_EkmyV#9KC{<%dLxn9K0 z5mvQ5-TyCs`Hr{;3Jv#Q<1~ecaT)^#28P-VX&XfT|9}4f|9`dr{}=rK|M&m@|APPj z-@O+hv$i~Cevh9|%$l$ne{}CCFEY<$75&k);NRQ%_V@hzzAorb40P+Uzx&VMVRckP zMV!@Yd1l{R=XVKjII!>k4(92i`h~Xs8;{=l%+b6!t?}P2_KE{)2Rw_jh0kBT(j@;o^-_?@7elmf6c?Q$_sjxxi1L6NX)*zzNbh~sgk{Sk5+X^k#)!8 zwnKBDRovZJ;`(rT)S5j9{(BcJ_WH5R`cdKH2F|FJl1CqB>+6^pO)tTnx z)t-kY@JTJ(d3@S?*9Z3)_`a{Zc+YrY_pJ|}YZl*pW;JnZoMDsuJyTvGsY$DEzv^H- zbyviZ%@Wmo_tis?Q%VC!?KiG7{UkpH^DcTzhA4GT1!&o2x75tJ(N2ys~Cb>K~oth4qW`_pB@H>5A72|NZYp);zx5+a?(2e6NTNm!J7^ z^$E>t(W0%tif^hqR{!#d39_%P|DB?2>vn$c_V6IDzZymiH!sQmWphg|pB<8YD{{#( zNlxy27I(D1N5o#@N>V-6zrT6+@hwYJ0$B1LzWgcM7whug;;@+R+3SbjEAVamF8biR zj{VyGa*Gg{>|2CUpXSm;nTkmh*ORlNs z-WdH_%(rM>&SXaZ-#kohce}TUHXE#~-T!!p*#vtti8IG!j@7Oy7k;qEakj>XKRk>F zZIfp_|7y0vXiLPKyB*ag+qUg~s=;Y>ruy!OYiFx(9=&@@zPiM+*oN=F+EaGt$*gRw zE3PnqW=W7>FsNZzk@29BLt*)oQw$6RF1PnHG5=Tpul3lVzTe1ICS05Ehv>U6|0g7! z+V=SCufI*juOy!{ytYYgua;_fmY(PQ^&p8qP><8?6lQiqsuZT3(1FhzDZ~Ly7rcV{y^iagG@>6Nv4`;K3Bzt6he_`LS1 ztl)~$Ehp#B-?Lu0sn1U7Me>!}_r6ixo42lv*it|L*t4ad9!dSv&RAHLa{IT-C8rnP zTtv@*^(@_3Q94EJw)FA~shauK&f=RU#Iq)guiksmPiU&RgueSOE&FZasR3^$tywew zl2>fXg;1vL^5OAsHDV9^?N()W{^A?R<(hn8x6W?We;Z%5Ok(Nza>Z9g-Kg=`gqQIf z`yDQ?<+%O#)c2YI(blvi$$0aB-4|p^Czm*$+ai0s{p-%^{HuvRwLhoSU)$e&{+izE znpbrt#V%iUpN6mSeLiEwPv`efH_zF1uufO@^ z&hHC8P0KrMzMYTXZ@S^%?(Ydk-){ZNnIlzlZNL8MS8fX@m%kTdxSJ8JH>Hl@ji9_t z^s~>KVmvn;-)jE%cjC2+XTli{oMTFu!2W@K*4IA`Y6l#)vL$$YU?{rXc=6%B0}c-v zi%#6U8w|9y8WjAdAPrXSHU`M>Mpokv_1l65;i{(kfAd|3aVx&3|22Pa9k>9Tt!Z&{mo zzj7w8gV?%dhr}QMoms^B-0@n4>SM>(9{<~&KGmPvb5-|A)6(Tj123{YRh8Of;`QXx z?MC(myC*g??OZ%DLu26unT;uOO&becGZH=3o=b=C?-pv^K3n+Y{!d5sQ?^>=T?pB? zI`(69xY75PxHn(2O--&f6n}kH!??sHqxRywugfmpDSf_ob5`2~?|G}67~?trulM?_ zT$Qu>)}{&ZBCD$Sm^2U8**2|RXmu|BwWS!#lBZ{v>kEp1S$_NL+1_JCTkQ4yUN8Ik zb7I|-k98*__Ig;wyl`N-{ms@-D`G)=T-WOBHZfm3y8oX%dQ|52v9 zf6|X#R!_@bXbVSwf3I)%@$9B&hrEw1EbRFI_E5G-OR()E9Cva4r6?_3 z$!DhN^KR~`nV+bg;rD$@-SLa7HwsSE7u_O~)OynVqtiBC&p*QYMRt75owLdc4}4`b z@{nZM7ZI~Ie2My?BLH9-_!GanXA{nKO-QSY}46axqwONWz18FXbYaX797#W4kp*zSvfClK3O4E zyS!uj-76nYn{c}K@@|n!_`a1n^NZ=ltjrBX?;B%&TSfGqy|?YpLd#{Xx)*E@hBL4= z6kfFbb8s^I_9jM#j#a8zbLA}tF~F0wP9ZQFWoj_d5Jw;3W< z91d6LlU{nZP`YdPAD#8r7hYcZ^gmzqoxG^MiYk+fd<%$10zMm{os%O0uWe}{tLv-`LYohD? zbLZEUn9g+GbvaGey!_WB&i~n4S2*k6cPoF9Nn;&|LxOM(IPLse}|?<>h7>hxx&Wou!>#T$L!CRvfc;bN9Oo#-+%e?)_GkFr(QqG zviVUUw#n-GJ$uvc$McmoZd&}xCfkQiq50a|>TLVwIK|-OOIPrfP5QHHK~Ub7lYiMS z%$9z?^jqy7mA&8pKYnBTWBu(%?0(&!**Es7#QGSXJKd(a;8o`hnQQ~mEN@+0?tiCQ%K&yo1TfNxVGL#sF$ z>yEx)YO$@3n0z=fMSTCwq-j6oZp2Ng|I8%G@Xx~Lr~9#42N=3fJ@VYA{QT1%4OdgX zc#msu{>J}bI(dEiU)zn7Gk%rd%>H}V|4{$q^-Oiw`jZomtL0z2<^R_H+QZ|F->TON zUwBb=K3MOW_DaF&CijG9A20O(yRor0{>0br?j3(?-pgi7Jbt+EH(TPRCpU_D&Te+U z{8q+#{o^cWt0y-kT<6_MK5}nkTuF1JRMd3cwR1n;x@olS+4r&s8{a?P6>j$U!t9Lo z>L0&cC^-;S_f2$?#Li=7mYbw~{w`u;c(ZydeyMH@Z#?bdA+`QDS?%V=@4a(7kd?do4;)w`qnGD^7*ZN0a7Q>0VWZiU{Z z%M@M}{;v9N)4_aMb4@j$R$RBX>C?%|zccgawq4~uR~zovcle(74e<~E?-#6a6XiR% z=87#_j{40%siz}a4DP*X-hZ_Fdg<)BM_KD#7};WcckjxU0-_s>ioa+4!cf^sx^8&|LdM#{;SsI&E3lqG4F%fp}hS0 zHCk@jkF{?8zSzE&<@OqFWmTh;=Y56yc--HukKK1|-*drf-Bx##3in^HcKo)n`{Ik+ zfeKIcvmgKebYbUO5$~Sb;E($^3q`hkiI0B~X0w?)^@xz+kN?$Sx>p@;d7XA|H#R+L z%RgE3gw`jHwz5zWCG*VP{Qnf@98HT&_s?21`Gu!%t*Ymln3WmFuBE%365qXY(KVx% zzpl}JM_%>0h1*ZlU!vVr?|XJ_c=m@~-+iU$tPfQLvs`^uMf9rAA98!{ zU5)Cm`5EcGk_Vt6z?Lc>LqYYl&G<$-}T=n)aJJUksmr zV0n=7AYyU<{^(ws(QS<8^_fB^P z-W@!v81zm$2dw5{U@$O?J7>gv&gSu*;N;4uEq@=G{qNan?viU|pXAZq-V(~uyibhJ zJYq#{W@@dSXNUW*i<*!e20gXh@$?$x*aqfOSHU;2Huc6{oz z+0XO;m-R25|Nr1^pHG`-ZCM`Le)t5V@pRj1)rXyDADi~&QM=-!&zqKC+MCa(@ZQku zxKjcb^H!N@TcQrF#+*&+=QR$t4j{&@eo6#vw_iiky|G`59e1t!`&NJ2-`veCXDj`t{j*a&YxTPP zM{KCxdfAvQJG5_46o&mp`mi#_WF4RC*IB6c+J#GUpp?jI+k(GVk;Tt@cTda ztWAA;-KvIjdtt=gs2-c>dG&E=Z(es@y#4NR>Ru3ihaA^mQ3^RPYP)9JT9y5+2a+~z z7I_|XJ+iHPlb8JVfbP6-Z>=heSk|fD8-uG?9a&M-K5f?F#2Zybe}(_ezjEl#7ui?E zOibw?HB8i}W=vw1yI-fU!Kc7;?t! zQ{M;u`@fsbPMKeEtIqGBn3EdkzW(s9IrV*p{iC*J>wla}nz>(?w=AVXFva~&ec45$ zd$nA`AEh`}t*)APXwyakwY5#vKaH1P`g!T}$}hQQa$UXCPPT+Poi4gjQED{d?)0@% z*TSRni`YVy*X2F!*w8n7`{xOZ%l7THT*>2q;$_I`E4wu|KVAL($kQ8fYPW;EJ}91( z`?L9_;@mIK`@7S=vNqlPu-u@fb3R*aL3H0!`+c?!_21jG|E{oKV#64R6RjTP+_pSRfT`Fwt|3lKnbJZ&2bKepTyHsF5$s9LmcF*ldofrbadF?>fpZrbg-;nA)9!#*Xg zh71m&?Cf*XofsdTX5CS=Xyi821MJeHPOCd}H0uq_(9!9-EZcZHfAz zvTf1VBkN}^SYgO~H!9&iLqk*Otd;)9qrZxza2z<2{PnU9bL+A4!!@3XhZuJ(dmrw- zmWh4KnW|%qnR_+vW`+D*`+V{LnA#^i7H`aLW82SnMSDrsoG9Rp)#T0c`B?F}T(-Z} zBLBLjiTd{ks^{!Bm43bYHa5HE#5C{CwTVZozb>51tk-`1-uCV1^7y>F)_Uvjx7Plu zkUjs?Gbe@L|LZH`-p!uF(zM@sUTu|nT&CRS>tYArU){fc*75i9-qX!>}u`kp2cXYqJoMW?VzUuC_Rgq6qAAYG`+x;!(-)rmp zpU-A=y9HLgcFl~~k#{6*?zFb-Sect^mS&yel@6guPW{9DJ3z1Z{WKcv62iBZ^j z_rl_9pSr%DZ2Rz4%XaSFa&6WxkGyu8|NUu^CcR7d(ERLuPG#4!4XgG?eZDxs_~`QK z_wUZ>yL-Iz`t4sc_g(*zpE6hN-?zv&`sd6ZEjv*^)v|r>zO%E0*pzlxU98Ei&N-jZ ztvai6`t4t(b&)pdmzSN|^J>}YU(c%FOkaGnbl2IVMUq$gYkuT?)HnUHpWj7HU*-NJ z-B2~3W6Qt9&5M0>$@r3JaqCr@5r{@>wfKIWteAKBd~r+RFsU)2m zN_+j~eNTbR^r_EEXYWlmS^jeVRg;LrF7a)-f*0pR8~5EvHvM#S{oxo}L}?>JPuy zq-|$$(=ZTvBfIkOr!>t5wgiTTiJNvS?oeQ4aBw-$FLFuqab$6MNv(&mihb5MsaL79 zmTRX}E)H9{bB_Kr$2(cFAs*C#hzcE zTDqQ{xvIKe{$K6N{+gGwD}vq$mq=gAWm}p*^<-@!o3N7T^kARQcCz+=JK__>igbQ$ z(tQ{A<9kN!+Rx{zCtQCnvt|3a_64_og# z`|x6*lFYS-?d}V&K60OMKx0kn?_*cLKM~}J*%tpQV%H8?b0#I@yq~%LUn|38R`q#) z`=A%Tr}oj`?eE&$7H7mh>#XA6IM;C92OiE$gV3Jq&9{%nR>@}>Y~3ksqc0W7v**go z{kQs;zmK>W+G?7i_w4x9dmmrNO!-l`v-kbi^!fHq)tn9<-OY!t@4NMsDX9PNzc)W? zWFPWAp1fW2(@xe4?5Ek)_?li=*!0f+c-8Ioj&Ei6w|XsmR#yG=>viGxmTTv%{_^9` z@2EFB{(G@I_*V7B-bnfVbK}h$>LU|sGJ4%4tZ&RspPMO_t$yPD z`rHTizQ6lur7hKy#$Ovdt0V6~;oAz`uuG+}kBV+)Z#%N1G}hQt&B9i1Vzu%5weh#5 zvX`V)7F~2d_@{B65>s8e^m#U&pPZIv38(+vb7f>()HF4$hntso#Z=y^`m=WrHFb+M z=&ieH-ZYWRfmwq&VAcmtJ=NZsEDQ|{^#bAiFLv#jv@4&#J8in3pVNU&ZH7L!(|C6G z-kP(8MdEphs=wS>uKkay%wNCopR{VmyUNJ#e0OJezIj}p|9Il(S(*nL9g=?VDa3S` zyQxO6RzJ4>`5xw53(SP#cJDu-wc(t|+i%&iotu~Mj+}nW;bUj!t#`)yWmiwhY<>Cm zVT9TK9beDfye6D9tNX*s{i{sAGg#iay=|?D+p4*TPI}+3`rBA6v?F4gY~NYV$BSOP zS?suIFT2Bw?OT_W`3w72FSUBDR`ld~{ldpS8t3=fv`|Aw5EbH#42N^Y$T0F{Eru;sA zQs;cm`&r*Vb?s_dapm5vQnp+}zU@C!AO8<$t=^?kyx`=oGr^yd%f4v+N>z=R#}uvCQF&kX+lkkIue0sm%zu$_xv$!%aE-VC(+BYj9A-N_w$i(} z-Ou2g)Q_)%Cu&w33vlI4-lAN@$+Bbh!Rv;TY#2-zFsn4PH*X1>pu)gVmG5hpQRTO% z`qDA>GZ6)!SF`S2z2sc%S$XkYz2<9IFqV6*)r%3GcKBP9^m@n1ljVHoyL~#aC)&;K zk*!!?@q?1k&6kAO75>lT6@Gv2bo}ZQ?{6HpiQ+4~!tY|kCLp`6iaB)m!HIL2lfJKr z`=IQ1-D1;m@5g;FCe2RE&N#n5tM}La|2zKXIPX3wtKYs>uKw+!O1Dfeg~i3U;`lPA zEHr=h;8oH6U2-o)0_A)ytMg@AF&#?~!M@xcl9!=EWZ0r_Y-!ebu{4)2gB1bA8_Lhe{<`<(szr zIsLn-@olZ|+dF&njK%CG@bVk--+URD{gx^0=)5c1j_;qftz&ciQ~ufMezR+RrN+gQ zrP=j$|C1PXUT=wbeed+;zV&KLzj21Z& z+_Rd^5+Bn2ogJ)7{Cd_!%6I73f4-~0r@witb?oenS+>vL>HV7i%>CafTi+ATtX`Xz zexBuc<<80LQrEVetugiS=qQ_i=D1kj%Ugmq&u=bnX|R3}D;1D3f$`rD>z(Cq=7?D7 zeBiiK^qyzy5+1J)6BEVtZ~iy`{X0fK-QrTmeyggQwf}3?o*Wj|x42&)Z+-0X40pBt z@4wdX&hvS|c`QP9hJoktufK|U?>W0nU%RoI`FF(~!ws*FRixcZethed`tyBKmPt(g zh5o#1YCR|Z+5J0Pvv%2e@9ej-n;HJzJ=@}qwLsn>j5jr#Gg&S^{3|443ZeR(HorCR2qJsdIV^7~bu z7gauqwcT;>$MS!h!tL`HYkd>>9=|>2#Lidi&G-D>Ufwb@;Omoz&8fftq~=Xn^2_31 zpIM*HXZ0U97^ zcyq`h@AB%zq_+p7S6o@}_5c0Zo3o~?G{0BiX=~a0;MDhD_KT~JHir6H?l~O(r1_gC z&#H=Nx7Kmbo!0*FtNBCK#oW&g;!gdW&8alSa}Up(;2W-@Zu~8bf23-2H$81nYrCPb z;n4rdC+g3A=QB5P@LsuNk=XIG-Hek1#pm$6Hs!Bh`&K|xNap$`&M2$c3o(+9R!#r4 z$dmVcNAL;d2|1fsdh%KCFKr9^ZKZg7%iS+*{pM$XZsEG|bnjbx-`i5_?|Qw?-L3d} z&C}ycV|cRkul!ye-d9q!Ms0nd`>%#|Z-2cmI8$?ec1f;4uJK-;Ieotrk5*imy)<>} z9s9267SV6hJm!m}q^^&emw&FT?BA-eZSUECKi}MI!THmm>b2BlyV!pHdt2hayj@`@ z-??i0&Gl2n+F0*pNLfWK-|^c1+F!5dzaqBxaxvZF`~B?T?CtmdEcyR6wY=N@^Wuf~ z@2Go*{rh%De&TAiPlfDrqt%j5oqBfn*S58{&m5mJiDxPo>=2rTi18RB1_p+RCmaif z|NnpV|Nq1P|IhvZ|DX5&|EK@|pRu#tBe?#_Udg#vMHu9o+duWq6rS59X~+0=z5Tz% zFDEYMU0>mMDoo>Zy`F+wyLavS&fCeq9ORYfpArT_P& zG}rFA&8zWd%4+kH>WYVtaGkr-S)B1mZ4yxwa`*# z7Y5dhR}8!kW#MX!3dHGeyHC;<1hy9ja z&(l|(4hO3@e`~kqmulLws5)YiP)+4z=KlxnnPg7Yew6xPbbak|pK1THWGk~SOZ|>_ z+_#O<)?Ih|hR^S6i%LeJU`{=oJ~qG zm!sv@Us+SIwkGB64lS?t4GZ2}FYmwpuI0&F{_pla8>XuDl%Gi#pSk+w%Y79H^+Y7b@8)yv%g&5zV~*}TL`z-Z$`?9(x2G%VViqD)pY5e+FS-#-EC)W zTIH)>%3s*UM6$iOdORimjLE&U zuIcw~MX#vap>1=t_rBMyH!(9OPGq>3v1h{hCG(c}$4Y8c$u~#;mQ9k>mzwi~dXN*rK-Vd+u`}lh6_KgSMoaVW=TYef_$v$h(e4!hESvVx#KL7n;tG%`2z3V&J zvY%Dlb>IJ@nxp>L6F!D*VZW`fPdk?MJfisi`5ANK8@Vm!3SWHnaBKONvNx?Srr(rv zJbdHcmsYiVJA*AbukYd5FoErB*a!Ao=XZ)LUppIjVqX1BL=a#s+&E*9DF4 z47?3isX`14LSLUvayelyzHjH9RM%~ro-pj=Wta8%czl^u_TB{J3Cs`5k8gjxL|tQV z)RcWoes4~@aA)qeY>v#kr*a&#$~n#ILOyI!GGc6SI)CZ7xZ3UPkJtD7JoDgX!JG5> z-@DmavsFyP?dEy#E|KV$IaD}T{K}0d)!+Bcnj>Rl`8s`Jg8)-TS!Gqw9vuKlXI zAB%n!Ok*iGzO~@EWqsS0UrDYn-_N(t{+?V_lD*wzqSE0vvaupnpC3Ouq%xcjs|{C%?DjWhhb zdv-wVyX4}0)4x2cUB6G>GH^pq$%-MF@Q@2lReVrlg$(^l^bea<*p>SmtD&P!^{)5ka<;y<1|Eci3Z{ut}L)i?M2s(PJm zlKdtmT2HXynBcXxyZqH}R&Skk{^2y~H?j{z&M$ize)mQ8&6^wIBsV+0DZKl*g7t>B z_=@-?p{Xi~_X@=gPo(br_a*I%;~TkeuQ*Q@oUY~V?LKw)z2&vMXm68mtQA243z)Yt zsGL8!HuMw&gF{|Ov`eh0B~R_gdD<(59oK5r`l~f5uGv3{J4DGhvLJt1SYCUZF)Wbx!KB& z-}2GBm4lZ*jyL?X|4K@P@z?JC@_T+YZ`ttc`rC3Yucbdv$cx+i+lkxX-v8I_-i@_a zqVBq{|9$%PzJJkwOG7tiI{SU=h`z6T?QHJ1>0)pG9^8NP;GFY8+x}aL@v-dPJmp~&vm+ZLL*@$x;Z<7fN$E}Zzc z*WWmAO%~IRpT4_Sq&|LqweR_xkGHsXs=g}^wmGG>_toc?bTu2j=!c%$0?4t^v+)$eF-hZZ%$d)%QG3&XEv$IaMN7peZG~}d~ zO%m7H7hyAH{*Am9i_*H^3syX|ORAb2R;r>}!T9>yqObcW9CLJ&-Tv(6k-z`GUzby` zd)8K0bL-gtO8T@+IfUj&~9}k5`o6IIC&&yix9Ydc3t^ko?`+Gd+{f?|WnYSB~@3&hVIU zh2N!iJpBiCbnWKt3foX8aM)w5!VE(_@kRQpBa^fJS$K=r+)jGlWm-1*Hpl6#bvsMS zeYxF~9l08r8V`msgz!2r6nR)ioMvD+aA-lBOn=GU^=Ds8&%A%=`Q5voX8#w?zp&}| z)KBY5)$POIajb=SW7K1pox;(gjXKgR^kn^-?jJJ9Y}#G_+Y z=W+`-n7e5U?EV`WR$zZUSmd|W*L`=Z_>RZRy_p(o+biOG`*fGq*{Iv5?|-&e7{ymy zj^n$|wpjY||CeFB-;Si!zGK@K(qwRFQTl>;azFYH-Hy%tT|NEu`R$>PI8LuUdHG}W z6|?YVYA(CO7qmV7u{`u)&YZ_L&Yzoku7BsN+Xfy6KU$32+UNBAO?}1|`Llkj!Pj#M zv0*n3ZaVkcYQsL$bK*~xZyM)+ED_1!=HPvBWz8$^qU^7Iw$fEUSI>BP^x)Z*Gp%DY z-$V-gYZok9dDi~;9})hjb*a)7ys=-?jUR~5+kd;Vc4o|bdp@R(MN_N)&Wv6vXY+eI zpFC4^z5GQHpC7e9vF~#swk!N}Jw@%%8kC^ixUz7WxeXmQ-WP{|dn-_OI zw0m^rvKsr$cCL0Y4{5nfn~VNF z<$InO&yBm&dziao`gC2*m^M>CeW7RR_IqliZf|C{etzuHNAb)5EB7(o_{n=B`paF@ zxeqV>ExqDye$ye?ex?J*jn~Vc%T1hp@yo{|mJPG}On>b#^L^7Z@!PcJ+*S><3%z+W zCj473aBDjAo2zeTe2i(%WiT*3_BCJZJG+!$!`A8b%q~%%Z&~m7X0pt4(Yob+&#p2Z zQhKTIQpDhZne9X#20MuY#ta7Yr*&uYyvy%pURPc5ExO1|JQPbU;UJs%5mlEuXF3<91N#0 zKbZ8dg+Nt~RN#DkvwwlN6 zx4uq&d*z-=*^`7d=g*lMullCmx`^*L|Niaj>u274cq1@Eisiq)^7d_S(r2#kIH6V+ zAZ5b#@k^}wnd#|EGK;>{F5h@3H(}@AuB|*LN)~)wUR9g8Ygb|AvD7=&_ltM`-SE*a zeoA<_#I#+(kM}oTP5Ne~XYa7R#(U)k=a1(;tY6Hx^xbdQyk50$Szqoh{}SF-75`Dt zDy(kFmj@XMp0Dax=aha?+n#h(fY-6&>!~|cuUBX^uZqz7_uO0Z!-cbX>LpKG|6P1p zu>W!5f!cO+y;RHmQ{Q?T8z05(Jd<@Tg;lbaWyK6{wbZ;hap&jmm7LfW{4Ff6{N~h@ zvq=ljJ<4odBF=uyN@i2&wJ-aQc3j*q^56F;*UMA7aWz(*TQa157Yl5fe(lW($F51) z@iXc)4_;k=Me1ct&#c#pf74uJ<;;J5Uo0tl=Ko%aJ2jCUi~l;^$&vT%`^9uyr04XZ zuS=hQy<)ygzUb%y;mb9eRf6~IZ`OprN74foam0tbAjm2vLs&<~ol# zJKyiJ^y}gRc7KxphP~-dP8V!@|K{uQ=l^pUUoRIn`L7VU zdq+&qzOU)-QjZeL4L!T{J z9)57f{6~Xht3R(fZveA|7XxnqgOeYtLPZc81H*&0R}VL>TKDv^^-HmRyGrhS{^xmM z_PVs3^gl0IQWjdQz2SO_IePEY-bXygHRtZi*m^Zxw_5x5)@`{d+W-FFxNuR9edfCT zObin@Kh@eH*1MQ(TF$oB@%J?DU-kba>c?n!Pj;E6Mca^!bcmMxw>#8b0r|P?Y)lYt`bp1kA_RJ{T@anV!IsIEotKPnUJZF;0 zk&4Xg$@1kl)OQ})ZWGaO8NMM$N~S_rrQ5H~pv7Lt^KH6NeB74{B>_%zz8`#e^w$2& zj=zl)ri8paQ+#9IC+8m9Rr}0l82LNj`EU4@rF@s}x_keQN_>?2@TG7EBim-NjY%H$ z^U}bQoxk=}actfH>F&pDczg-yRRJutv^gLk+8-uYV>KMZa- zXZ53e?bYy-D?fF1eR}#y($({GlpC+s4y#Sfi{~DSj;?KYx^;i|N&#N3sVO?qC$7Kr z`rzMEa9!M_U%tKkV0TuHzRLXnFSERjOEUQMxb@B-`ZVp{+MBFR;?rOEK8ie8dNSk7 z^`}cNmuxvJA^-Q4%%j2z!xztHP2s=iINNxgy>7&{@HuUg&i~eTHczhp9AkF%c=eOp zFO|Pth8gPfqz@}(bX-Vzb5c*P%HtC|bp6Xt76uRiU4Ly1x@GD19%+&P|Ns5}|DXN; z|09qAmw*5NuetYQx8VGf(SdSdj512?>c3=nTN}UjaTk3r{cPsPFYlIbe!3`m@y6Lt zuX6sLU(fuIBdYq_vdE3y32W#4x#@6ztC)pf-dQHuFTW~ZaZXWdsQxAqarpGL`Hyay zRI^N9lFuk3xj$~FS?yc9x;4qG<^P$5EA4*&W$wb}tzZ311kNysf77_d6!LoZlZ$)Ll<7u{oH&OzRJG$1R2p^D;yha0e{cJE-rgJIL zPF&mk|KjzWzKqy)fm3IGjpug1)AG3e;)9=a4m-WooREDsWoD>Q@9(ZZs&BeW?V|JT zuZy2MU32Q)|E0SAZ+REATv~Q6@!6x6I=v{_FwOh(L*I3z-1q1WKcKvD<(xTB-z~ep zH}bT`;Wfrde4jVI+&VXIj$F@|iE3Acr#zb(G<(h3qZ`UsN>3?O|IWE+kALc{qwe8R z2djh46OPtNJy}^{eYM6!U#nL+zVY~_ zyzN2{_g{Rs!}z*F+m!#)n0t`bT&a#~hD-JC9?}pP8=6=ZmE-9~~Ag)MVJV!n*VQiECThmu}e5 zA(MBp{FALm$>VJg{xEHM_sTwZ)@;+bo7u0Ht*@7kf1Z7xy<^XPgI_y4_wW8x``@rf zHrr;q;ilx=4fS{B#7}I>Z~typzkYr0SDD1;d3-nZ8~4<7$uw`i{_1a8#`@EOg}>iD z-1fNchsAxPj>{$YB{LoD&prN>^)tLaYWkHObN=pn{$fM*`50T5>qqCUSGI6j_HMHg z^NuVXTZimbq34&`2-q&%|9fk`;f=#5PD>o0yX2)j8~2KZPk-hl>@V&<+O=WR)qQ`N ztZdglGuS6kwB6p|0DHj86H?9G+%FhpwF< z8{PP;EuX@1OkwukD`vGha|L`>*Hme}R^a=Q{5$4$q{F?W8jFIxF_(Q#r~X+z;hc=a zv`f>3QkMDh+ou-ZQ`cMen%AZM-uCr}T`H1GR~lt1YyCfX%=?G)+k1YlvTr(PZaH+} z*yWUYKjwGeFN@ohV!eIQ6{|fTqq4xn?CRI z%ja3i`IVm*T-dwKbc;c@NZIS1Y40u`d-YW>?c*bt{-hTV&u(Y_^zqW}HKyM6J0o_k z)?R({&?A**w{KB)PqZToSoD?&=oZHqPyf4Gq4>1C+F#+5Dr=arYcx9&;khH1;znEW_(PtCq^yRyry7S+cG@76y5Hs@c0Kg4W8)p{Bo_W=Zflpo3Hz3AM-T(EWa$dYKL%k zv!e1U98*k6ZX!2BcklfVRPR0Te=aGhF?GD>X*|~ zd$#p1Q&-Y+n^64b`1Q4sUuA5+FW($-w^Cn?!SCJrr0*;X(~I98zVthNcJcEcLZUh| znpmX+q?q~|pB^gFZNABs#rRl?;aMuTgqYcbO;09#zQE8ho1I|+!w&0EZzhI@RS%^* zg8peJ&RBE%Z1Tkq(f?hzGOVvm`}6c-iS?@$6BrnTe(%xBVdPR@yGX|Sjo9M$#nGnK zDctpO?aO11d+Zl2VmL50^-i^z5HC}D($=r0n$|*5HsZoFb-e6t?FzZdoW9Lp{WLtn zlwmV-@B58n%IU{~rv$1Wv5HyCnIe zqgC(gXqh!q)7*l;Zn3&v)c?=ibN96Q9mnf`??3w5Krd{H<=_14cJ<-Um+7mk)y~>v zxY@Lmi}B)0x9{P-{yP)D)_t%)yZmFqvU|6le?Rr_|K6A|-uIs(6u;K5l`MR*<;qjO zuJtanD~%p6Tkpij`Bb)DC4bJ2soO3H?UsC)JMWPCARs?|SV* z`By!jyn8dtbF1(Jk!p$vqc@B%9J! zJ^6L*+qct?YbRAcSQ})KVY@EEQS^Pnzt@5b9`0Z6De|EGRe{XHun#}j;!aracptN6 z>BW8~7q0vV>MNTztoj`>XRhAO?Ea7Ywf^mHR{p==Fk*YOdCa4m*ZYeZq@H;(tZ~`Q#Rl_YjjVgZjd^rpqP2gk zEzRItpTjv%o~h_-?jA{oUB45L`EHoNCc?_y+`v79JD~L{`-7WNpb`1oM?L1M3AJ0F zO#k*fer0xyX}EO6Q5pMtCv#5s=-&AEU*k}0%2Hdesr%I;dbvM)SyK3pe2w4uL*?|hg+Dq< z<$sxe-?c>|G@xNU%`}WTzukHRf z9hq*me%AfJZ>y$-1q-S>-~S(c|6r7=;x?JTcIST`x;fGP&4r53Tc5sM^!9N2zAT?@ zxjXg@BYW%J!&ayZuK4d z!2Z!?^@N?t@o!h}i`stoc46SYlYh@IyChw-=WjrC_s#hxZ!VWj;|kW%?ct6-d#~uk z$+LE)ixl1;zxycn->T&M;gI~K^x3EaHc63YE8J|d{Jw^Dj`^QCOxXJ^ZTw|N(R{`=tEyv-Z8guCT&UTQZx zAoMhAV#tf{(#rL>emY)CQVZu-74Cm$wAy)}mE7i|(_Qzq^tP<~7I98eM||@s{abOn zZtpuj+5hRY#Y^86GCj=yF?-*(OTQ-;YH)vk_W#k#B8A>$)r!M)|L2{U@?w@+U7|K;7+`gi7?4>S7~$0Im%{rfom_pFPV|90(o-X{&vf|{@sh+rr%~Yyxox^ zX=T#)yXdFPW34i&vYs1%f9=$teQpw$$Tn5!%6A3Q_J?}PnA`S?_<4Vj-kczCc9r8| zHlbro`c1#KC&wGSkJ;G5`$X2QK`4Mtg!v<*K}?-S0=t0!j*^Bu3@fs;;Ql-5L@oK}&gi{_h7I?Vc)#Yoz&w028P z@l~~W>qL&MmA5T=-hOL5x3ryM)s=I;TZMl;Q?5IHW0t|XUr{$M{@yROV{38QLbD@k z|EF?FU0OCL!0KMU-i0fzm){&c_=fMj%5~3qWpUlBb5sA?+wlBfBo^`c-SY0h%Zm)c z8ef_JdFv6>^wiOL=EiA-59@0d{;vNRdV7Ao;8O3KuVk+F>mMi_%!D`c4j|;bye(Y{ZiWV|^7bpH(`t>&dFRI(~ zrt|*4b(wG0Ti!sftvTGEzx(g|sku0B@0ykG_9R}Bxbk&pq2(sN$nfJ{lGo3MH@7Zb z(Yk2+x%#{RE_D09;@$l>y|QjgG|Rc11G8*Q9)D`|E+1UQ2zoQYo6aS z6HYg;TrTPFw>P!4JoR<$|6h(jkwz>K`d~3MSel9KBdHdbpTAm9(QhGhV?)>zcoy~XZQ82#+%DqPj0ZBa$ei;`}x4PDZf+B&wPLJ)#`uxGa|NZpJD^K>GlD9p|1!_8TwHm|L~X^qtFth5wFJP0;&WJmX`XritADx$}QlylIwy zr?$Oqis;4bPyLVcwry+K@!oOXG{4O~d(U@o+^Uq`Q9N1QuhRS1o;B~T9yzt=+>F^} z|IZz}DZcmnIm3vXBGX?NZ#GgXTfO%Bf{DrV@6X?27X8n%a;`gjHIKxhtT(l1BPZ`( zZ!2+IyTpLI*gsn@FK)$d#+ex}>bKZ1J=;=r-%FOPa`=biiZ%lnZ`||a>J)VD`b*k-qqfg(`F149 zm2aDS>jPJ{`no>?(hcnU`YYc&6g&NX(Zw~%hOt{he?PmBTk-vd^!sL;|Et8rvz{N{ z(REGqPxU^#Zy{S3zuTL;Ztl{LTR;ANcqo5vM3`La+N8zXO)sCh8!hp3$*0gYSB=m8 zI+eeF{_U1~KSFlSS3kQZU_bLkk*u?Sd;i{aUFM}5`nS4&-9DY{gG(QjXx*3o|E8=r z-|$vOnDpg|XFs2c33~p5esWjhcgPga| zJe!k$KATf6;_$Aj(GzTfd~ zb@=4~rz^j|z7thx=~P~F*husA9)Zr|)`!xS-`$n{*!P9$!g;RcUGqc3ALgD4{9OBI z({7!pzCf|NlAu|3Ccy|9^S#!kD{uFRL5uJs)mq zPxae;J@e9cCi|OHgg2G?%#pQgT=sAK@c-*v_DXbZ%%d;dn)=6l3$GOZ|9>8+}XMK2F*( zUr8dze(|@dA{KjB3U8izO>g2^t$w*3e! zystmgv06_&-Cy8&?c&WmPn^RRI`7v1ZSmJwH_1DIvouq;{<-0q%9f4hjuY)=+0O2L zo3H;Oa^|jGyDFwj>ISoRMo%{h@_2K)vg@6~lVAtk{dN|5f;X;e`^_+3`0hbmPL!2s zzvskNd#vo^pZ@1uF(<;~am35CYrkVsPIIf>&*fP8-RbC=$c~^{h|GYzf)p=t&NOWnE6S5*GjoK$M}=!n{KXuROHwD$+b`N z-j3bc^^>L=>z|(U;K~i_pY?}LoYv3FeO>x*!Yu87KWyGV+qq6&uaZYj$>`U=ztO2G(&#<;*Im32cO#7r1$&xJ(=~gkDh;iZx%6M z@A3T8g?HoMUY-5=8^^DAddzb+?yonwHTUkGZ$-9o{AcZd`SV@8-X;6@$1c`m|K^zr znB;6Xl=}5>k}vaZzeQCoyM6wx3p1-tkxSw~ac$rJ-*x4@wUZ~>^0D^Y<$QZ^dh6EX zZi~2XpV(iW&#V2#J1eZo;`2g%#U(QGViKpKXK(CTP}F$D#Avp`$F_t6H?eFv1;a*B9tH*nqtXJw?R&SY^b}p*y=lVw$FrY$JY!=17EoR*H2=nQrW>~o zNtrxph@EBRUp>8}!qtYQV!FYkD4U2czS`o4+N|zvNH8URS?2 z&fd6Z(tGPQ|JqW|mS<_n#U;nhJ9GT-ftS-uBxI~gw*UXfxA7kLLzV0Iz+KOyk4 z`sa7E01ffoJh5ATOJ&S&T=G-6HYYb^{=L^Zi4&!cZrAp{wZ{15q^&n=p3Erx+iS(q zI6t^0b@qQT5&frCIZw*fB#jIt-a2k?tH1JVZNRDYJv#sWqs>d2+fF9+Kak(yt@y0^ z;Z3hcH;bmd_&?XVjs22n_UYB}A7g{%rTN-dR|bBl@H(_~#@%C4$1cD4xGpI-OS9_I zrTzQl-phNs$NoDfo~kqLvE+@IW$%7G__63nfsN_0xVsnXbZdXM-P`m{ZTcnSFWr^Xx@Q^Sn;(dvUwy|IsNya=$Fs z-u3G|F!lfZ8SCDxk-ltqE?(lt`)Bt5s;i%U=BlcVNPfO`D__s||H>I*^W!)CyYszc znOu=k&*I}7FK2vWySBc6JL99ulP}(Xy>)2e-oFpB)@*H5^OHTN@bAyO3H5A$pKIq! zW}9EU)83XGlm9*4zq)7YQK{y1nh;(xRdnyW(~BKm3!p>x^BM>(O0{-x@NP&EdS0-n!aY&A~|O%c>J* zWj$;Ezy5XQWNzX2f~|VF%K4|yM6gNjum2XAnLRDbs^p)z?v)Kc*?W@qs3&iobu?#Z z?0)A%r#>ak61ly}B=g~7jPPpA9F|J(fZg6*qa+v4-|t{%~;HLdzyxutdAZlOKa ze+y=M%#NtsBfqbvW}nXK+Us7A)qjMW8O?tz8h-M^n~Rapb8Gjw-Bst8TKhZUe4cmR z#+&!ws7T95t87@mS2N$Qz-g!g=4UyrOMR~|@??Klpm)oLY%e+XFZ=%s zi+?$~rYvdpx43_jx;E_V&ZVxt^k<{}#7h=0>tpRr!!IkHds@Svw{>xQ;~vd5<^Oo* z)z>+Dnq?+i^!u;>dsVT;Ryge1`RAdhb_OP&n~{=x(dLhE$+|p&zr0_cNO7L6$-bQW zVbzvx-8uPNthBoFsum`4GpBG(J{w@Bw?$0#h5Xl^k~KlcI`;L*y?=UJa-V+Qyfpjf z>QH&Twa@Bz#W0>L+W&6hN##)aMpw3}?78}{v<2lw*dCOfwl~}HZT7siuRiNmyPP_p zzEVZ#RGr@63nlhlkFH3y#FzPl?mK6?L)`un@CU!1{o`Sgth_1o+IFP=D!@n&-GR;wpD zb03)e-g@uo{r8{q-^sqVs@yMoJ;J(rck!9evJZ11s+Utbh{KmYs4tJgn1 z&ykO={%W#*(cN=0ckR#0`6ho~ERp`+@_5;fqjHIx-Pp5wIC*LB}RVN;E`m-i+cKaHKCv%Sn;SFUjX&mY%f zN{*NOSXRiXBV7A(L;W4ja{E6#57 zT)r!(fNwR!e5H=y#~1g#>()DW)^x@7rBc5(i+`Cjb7h?cSHsu;#&W5*cCHmWZV;Cx&!~6pM*RK{|D*R=op$?mI&Sr^N1t?cWG>%Z ze>Q6N^Qzybg>h{K(Hb}2$8OpCBsKJSx%A(jD^GX7@7`OM6Tkjk^{q#-F)R0K^TyAO zI`;kE?5RIL{Skh5kG_Tn&U^Incw)yKbI|M4G?mzxy z_2)*LfBT|+=e%;Rz8Peyd%gb4EAf^um+J#%^rR11{5V!VKQ_mjDm)v+T;=9G6s?6To_s&-yjXhR-`@O>E@(jN_-=_bN{8rlkT=m=A z=QHkX{I5E(I)85*=-TKQSE~zIbJJIym=IF8;+SeQ< zt3O{+@lKAER(bx}PRROKl-rl>+mGrjlHVk3omO^isd~1+wS@0#?_L`iZSa&zJM&?| zv=1Ln%Ie522w>!G*d4^gz`(FWcE6r+(B&Tykq?*g##%jhVtQ)ZV~^VxCl0ueUNu{}Ey@4v(?=)&U#biJAQgC) z>)OTI9q|dWkC(ds3EGqRX8B_6BF*2|+!oH2@cFd6xs7ADrT6)hxeB!(MemDlpC;Zn zNBi*S+3U(C{*$%lSzf+$jblXp&(MU&Db_bLPjIHb&0SFuvwp`hyG`f2&urE4zWw^& zsr;=^e$GBSZ_dismk;m1mc3g)@^19HKkR2VcwYEjK7IXP&f8CaJ(>P<^&6fk>tqUb z-M*z9{vjM!D*Vj-eDR;-d1=>f+AY8K_1O1EoG-bahfkDqXZgA!#q!$j2TGOp)?&Jz zU-SfiW&H17pfo|G(Zec~>*vO$Z)D!QsFC$}cOh=Q=ACEplR2}#_-}Wv-*rAR^ZMQo z|2Nn_+FW_z?dQ~)XJ52^4A#@TwXdSg?mL5#_`*NWbNUN4FD~BnZf5dr<2QFauNL0g zHeX}nmj=7{(^=y5JH&MD1sFt_JFDit*zJ_~sQAr;PaK|WZ`FMY*|?j(PhN;&7Egmd zW8I0}n*Y_NUJ!d-Y!$tCUF)9EzxNZlGDF|L%=x-&A4A>Oy=Rl&vA6AVj`;2D+w=d= z+g{@f{(2Yg-%q&vE^XoG`n$C~`{vzc`BfM(!{o8p>X(oEuU@!eG&|z=j%yMJu5;Q= zp7Y|b-A2h{`+Cki5n?>QydEf0IWMDtpr{``M7HWP2op1tII?xz1SdPj4u z|8;oVU6)#Ze(_0J^S}SzdqvNWf1Y=AIp?;0=l>SJ*7v?>{Pu5+c=CP|h0}HSWfk>b z|I#`C^vr|4hvBc*yIgyzeWr}_{CY-zR;?E?^*t_(3z(lw2{B-1U^p;!?!lZ5&E?W}ZCdwCy|M!~?LGec(oCjrU;f&^eQ-51R$t!d=jL~t^KxHt@L&F{ zt+TA?efHlJ&86$J*D$d9EZwu*yJl_F>xFYRX}Jr>IX*GoE$gXVW9oeFSJI;z_r%V* zWe-^$`yYINZuzz0M0L(j`|jLtIa2F6m6Owoq>uer|0C|`j*^F87vH|kn4e`Qyz$q! zYsYuj&%Wa>WOTdWoPF$@OVh70K3+L(MrgV88_mqc6syj%}$X* zQ*xK28N8L|eKgm2`Ayde^4FbSO~0n98UL35k*Rj!c8y(r@g40Kc7GAe`;(M#nVnf9v+S>umieZ?Z4=US=S6&#iv>^{}=1Pb`1W znWpP+Jm*32qTQ?BM=b7mTz`7b+np|(n3+FyrT%%toe;vxIO|lI*X4NW*GDF_D*Y^U z&viYL*U>*;r0z(`zmLxM6?aBF&d>gkF)j9a)ZJS_ygmD_x)*Q#t(g16y52)iivc=7 z^Aa&YW6Hq5pnp65g3$l}cfg0py!`(kit1+U{ybYf`_v0vduP*kNAJe|)lbvP-CEQB zUii9rLQU1vy?c%#T_i{w+kt{>pjpHOu1b?)GH3ZM!&O z-x>z78~^V{DCzqxmX*GF#oXU_X7FTNkYz_NG!&D&X5zvuSnn0SS$DSn3S^G zkHktR`+y0-#ME2_)HiqA#%-`<2W_;u9yX;5L zN3D#-i{H1b-ezvQ zc2BuzhhDdW*_-s`ir;qpZA@d=_n-XX;k%&FnT!k!4O;wxug~vSZL$r!Aa?Vi_wqmf z>)O8@T|VKGMs8P+he_GSjyZCU|3jB~zl`LF+3dS)?Q@3~Lz8cT%+fE@ooiD*gzWi0 z?RWLZXYOIQ{MLV{v`Gq>yL#`2^Yg~M<5uDieFi}UB~wYB%tmy}=E=sz&iI%rWa%NNNH5|1Za zZq3aUJpZ}BeT!=BQkFwAmF~@udnWwb?q{aFoQsV(zeVLvwyh$cqWC<&Pjgg!vgG4b z$E}-grAr*?@tw}Gea63(F6)pPmLUrp_eJ;X@0DMf^5TQ!Oa^_+n95it^Qj-}&!6Uw z))#a&+igDU%9jn_SJtGYt$7@N-9CTa?da;xx;_7+%g!H3za45SzWurT((A8xn%M2R z{r$_Lh*VSA$p2UK96$Zm^}pE8^X2l==~aiHSI;Z2>B(OFk#91~r)=iALXwtyc23Ai z`Ik4hXu>+lrw#!Z{hnGs+g|BEOMZVv(%00KSDUxg=(~$N+u6Kb*Ur>uPuk^Y-ZHN@ ziz?+X-eLIlD@wQF;VC=T8iu-#>QD7av6=Iy&0qGWbncT{f&XsPF6iAozol&VeGiN3 zJBQ!*N!{&9u{xTzqUE{*VO5L^8Zp-m;SxSINt;xV@cbb#%uU9 z|F22Gr8jfGi)MfA{a5l+E|b?!UNMSueNx_>JEz_TMVMe9p^B{C?i<>3EPCG@b+bJ(N7hQKSN#3FwAOa*T#Y-@%O3|GPI~Y7 z?$Io>;x{@9e&yfFmWu4Jx6rP>wRWaN`rk)ep1$WeboqP8I^AdQ%Eec6?`ONoCH&HS zF?%{w>y#COIexljOjqL9pRm~7W}DL^cWq(NUUQiY+wN-Z){8yIELUkbZ!6gUufFA< zb{bP$on_sIE6ZPRa`P?DoVMa+vhwQ7D?*~?uNVKF_ww6Q#|`;uN!&a4S6Jq;Y&~{% z?InlMwR`rfE>e$F+P2>5&a|wbSDtb1IouyG-=tRMUaCgE2>Yy4>V4&Fi)=!5lgd?J zWG2}@(>i_SNL}*$f3ntX7v_sk*I9Xf_pZhVA?cpsjOi7hqP#CDiv{<5PUgBicgb4L zU;6P+yA|uq7+b`g?GsiXnD#Zi#p+& zrkFdiH7~WF|FeAa`_l0fZ}#>6U-5Q>-1e4v@0S%; zOI}kL67?VUs`pdiDGKZFj%_J@xWn%+BE4d6JXU zzuqpqzf0|_=*9nU{AK#5KaQw>yWhV4kInaW%G=Uz&#K7o+1J*2EJFV7^xM~Z_SL`4 z@tHJPZ^PWFukWpuyS>ozO#A$n-ww$!Yu27$ll~4hI8Xnq2{MvQbD`v}duLJ8^ zXMJ*cd!t-BaZ3ET#nx|2n?mB{cFj7uEJ*K5Lii2t<;$`)&$myl(Vw&DL(s;S`=N0g z>MrlQ^y`!Md0P(i$)e1YFZXl!YpO;tO ztv+((m&9(Vdb{=SG%crZkCXP*@;AA6{TS2yA0LlNn*UvBW$HO`PVbg&VKqXwtCOuf z7ySBUka2&Zp^&AjnDr?tKAOONNrE&tE%XTPsm z{`uY7*!rJe0|LKIW$0%5L2T-zn!0%zyFb?}&rY0L&n0+nXF+-Jzh2&jx}WZE`L-*5 z!OQc0_j~hF{_ox>-~IjC>Gu2o&;MV@vij|e3-%9g`d8+b{al=v+4uANdbR%O>2qgKT#cB%^!ds7eZRKc zx8Jr`|6gD9pT9rzD_?9sbM)WL|2Ly-bAO#kQ)H%o?T%6Y1hj)?8l!bv(Nuq6rH&I<+QJp zRIF3DZ!T9o_cF=(=D+QGa?&@()Kt0u`FsBRCV>^|`XYI@OCC%pR#$jx6XQ` z-KwH`c+aZ*d%IPVuRonX%m0_+t^?=y?YVhhOro(Ysj7PX@rtr#ngy;SU?(?^QrsHay8NK>r z9iP1U?YeXSMds)7<_LY(zRh~TUDpm2{DTq4Wq?cbiF&X71Iw)KU%fkJpFFMMo%NgD zvHNo$zt`Whf6;flo?}%xE9H~dME=}=+tDV@>cNjY=kKiUKe6oNwZE66`&JsB?O!1J z`pUwH>P_EIeVsn>d)>aO+HWtEPtE;4xpAxBdZ}yk*6#hkv+msA=qI*BPP)3mP0PMKoW5Ona20yuE}W^BlttD7r%T39(<*RW>Q zq1{qeuJ=N{t{3f)$UddW_v7r_+(_2lTki!W%`f=XmUBSpWa+(mx;DAzBW1p?$n-g2 zv0SC}QAXxxXP=MvtK_CkQc%`3oAT4#*;ciBsn+RjAxC!{tdCw{>GRq8v{N_pw7e;s zcFeuUb-%t~$0@ac7vuixEuG~pKh>-5Z{^95Ns_ySOgmrwdHZ-)E8lgVs*vk)OC;Kj zW8RhZrOfo5xOveVoz3z&HLoi5UGqGu6U>$_S{ z|Bv1p`}|5eCR<9nG z6@Op(Yv1}B&$=fjaxeGqwTdf$J!j9p&sV;&oth9ib9$cB>%c7Wx09n3wfjg85yNKPZ>Xl+8r@ve8 z@wnLT%-ywTC(jCcQO_3lz1#6|XzuyK-Q_J`x30|odtvVX^RJa3c*I|bzph%Qf1Ix` zc~0ENbB<-&!fwXDWjd~%xO{K#`g;GI_5b9yue)paM)YRl`p36^bZqqdxl8KZ$CkHP^n{J3PF-?)}Oq zzrSnLowhZT6Yu-=er4c-wL2Rod2<|ibY$lKbthLR_*wt3=eK7KKC&?C!3XE*7mx{<5!){9m`OVqZ$$&F`)s(xzOm zpQpCq$KuAS+OpX<#ZW z8^YJ5zO3c{ZmakRW~=S!L7U#tvC6|vq|8lDCn)r^h)q;X`1?~Dy!(>F%c-`E_r^69|~B|Dux zY_Cnu5M~$oRJwq_wC4NIep{`{lg=z4QnP|=-dT{je#n*6o!TXJc?^?Y9L zfYZdtBjh}GNAEW>TU;vlzUg+QK>73kDy>3szvj1{|H|t0wtso`j^cmf$6oE% z-+0veU)8TFOR*zASZy{hUsn5l`+r-3i~s%CCMp&4Jz4VBOl*hC{o3dDe%I!GvE{jz zS@_z(?|0gR=Mvez*DRK)=zaCne)@7wb#3mq&G#R@-oa$EEpCtco!>V;&i!ulsp#*c z(wi#_?;M*Ip*Jg6;_s&3<;9!B7`}BsOTPQW%;anUgF~OLF()K`j`m95UEj1GM?)U2P%JUnhmEPP?$EUVmoTuR3 z-|UoYOpOuSO>UmP^>kBg!rJ-b88ItMKiV4#ntrILQ0X*r`hD@+g9j!19w|?)*MG>hp8Vf6+3(b5!+-rX zKjy6Yd31gM^s6b=k-_`w{#p8(OSUH8%wN}Wiuv%wDfjFOziWSqe$)I#+wSgs%9Af2 z0tvSfOy2YO9a496$3#0_d)D>E3VEmh&)(SL^56K3-FNPcdDdsSEq?yqaBObmlm6Ae z-S?YD7g+9l)Asf7emz_Dzs0P-UVQvhXW{m0=5xNsALs4i?S1v-$85pJ$7{2z;(rz7 zx6Rw@D4hR&{)6|0^4T}d=kB;`XH)kk`BMJ!LK&%_>c6w!RQnm2ZNA*~eAW?$SwCJU zxg6MV`PJsTx3(uH$A>MgZXV8>KeJy(bAyt#(4!Pd>pOPKJ|+B?+jgpc;gOrK^P_X}&K$pdI%(-e zwdFtlC8f=8?J&`kimDY}vUaWAVs`O2|KDBYS*&ICgC)3zeXi_topQ}bk?&alY}obI z(^@KY`SO47+G|ytZZh4SJ43clTwap%^=^O3?-{Y}^_NQT>ZvCMU!U`ct1Hwe`pKbk z%UL->T_N2|DmYe8>`cnLG%Z<(_2l}~+7n`8zUgkL)aqGh?0n~bN3DA5&HyK7YY9>8Kk8vr}14XinIQ5;=TRH`l>H6EjxMA zz8If(IHq&<%~FFW)jWH>-x>d{UH-{X)mXqxCtLa9kB*;OK~X|s%L0mzu{uD4Z3L%r z1_p+G9@U@sZ`NG?;fLCvIa0;?|MGK!Po@6k`*UZ$z}?fQw%vE`f23Q-3 zzTaZ}b)5IngY5g)-cA$#rFs2z^{I%BYb38J@2@)Cv9(O%xUri0>B1d%z90Ev9rtmS z%f*j-?ElTJH~QMI`a5D{#M!5Bzs-nS;~DqP{l~Y~rLVJ(l=a!!$?sg0{jK-M_vM{A z7GL)%2bEOw9+%zzIpxdm6J^5M3mU(DxV3|ofkpI;3Zyt+a9QlYj?Av${FL+F_bul> zH;@Q0Zu+mIU&By3r_hf(oORp05>HNH<~ftH@BPq>nZEMP?k8DGSpA~*9^3ZA?vuw- zJ_D0ivH!ImY-=iA`YrLI)rIND9pA;T-?LTjkCoM&H?i0Gou@_et><-CUds1Dq&_L( z;~$$>)=zjli#+YPHucvDzy08Ojc1jx^c=gJSHtCVWZuZV$^N1^L6$RZv+VYFo89K> z%B&WCRo45fnEmC~11;11tS zon8H^sb*fK?6z;2Re?#-9HmK9CEP8)Db93_>i)k(JWkj_g_@Iw71JSZ(j4*>}+^k!@ujv z%$KiiJYE|X-@Nw!ulTc{U%oIjI(TR6$(4@lR$i5O{WENL|LgwK{;$K1{ri2#R{Gk; zua93F{puHt%B%M5eH*v`?(PSbLMG?W*)4l+$Ef$Z_{YV6`F%h7ZtseVxBvfSUe$?v z)%!xW`~R-9KDOaoOU&hY*D`i3)wmGC=xGFNx42&1$oAY=-?H1{*OlU1)=&Kx$$dF^ zI7{}n=XST-6`kyNw%Jbi3ldqKv2>SW*xG!vm7-Ik4{055{c`of{Q82+Z&$o)6WKB6 z?C$Ij{hQS}bynu*t9-xke%-!1_d1?So)bD)rOUsOtL^=(h#4JuD&NzhC6^n$xIQs| zn%JjGzFo0j6w+SL{kQP+|Fi#8Gk?lNOufpq^k(OOo$s3he=E#y-BGb}qu6h~I{)eR zR(7RQi~l5>+)HNs{r1|%5C2}fxXs^I`gGa<`}LQ5^IS9I*Pd9tdQJ_`my4xJTmRLr zn7jQJW68_Q%k*kKef+(#He4_;GWXoV@CVaAKbNWRnJDyxd)k*JoeI4BQm!|xyz0sF z?X#NotQYfigM%gST`t_bt+V#@+~04yb~(mPP-2jTM)D}NucCUw?Bjk17gd{gpWhnz zUG1NmDdXR2fsOCX54`-mo-4XbNcrGg)BiO;zs-D*I#I%I$;CD4#p%k|Ug+8D1N)9{g<+A_X zV&7vA%FnNVzKC7$)Dxv|&!Yoo&)(-X;&^Nr-I=Utl@^z73$b8D|X zmyGr$2+rDf=kQ18X_eAn?kt^MwdU!Ekj=B?{iA07d8_*S_pwJh_v|9x-`aWk#Gcu2 zWfp3h7VAc<`6$)hx$~B@*{6B-m#R~5)Xup^dR{$!j5~Dq$z^;ud5!<+gbUm2^_%ox zK7al4pIJK%R=cf#va;xG9Ft`Ay3=#Q^4aU2ocpn2;>?RxPbPhxb@%QA{i8?I?wGDA z_g1KGI56dX)aIW*c{I3Lrw`*%7D$>PfrJjrNFs)1%o!LMT>BSPVi}f^>)3s(esbx; z4g1=UeJkdFmiOxZhL*CO^A5i(+a1sG>*4i#HanO8Jf&*PviX0G$1vY-{AY)BawbkX`X*+Oqel`fQbZ{*>K#Abj{_)Nb|a zt@EB+Y;t)wWuDwqXM01z<1yOz-yUAP(QfgJ$3GwJ`XlpQLi_s!Vavpx>i$G~#bcZE zQV!h`@ReWxLQt$IUHnuNWTB5kq2Bq&(GMSAlbP})HtF!gTHag1HIuG{tkaP>s-=5k zS=Boc7gmd;m$Sc1DQaBEzq5ifJFc+ufVp3sp~SzrQXka|BWtbyD$d`?`b^&L;y&(( zD=V`_CWy(-6c0JD_>1jt_H}oIr#6~QYUBMG-kZ8*)uFdlr_-+SpFHsO`STNNX89~< zPk+Ac+wPR7mfTM(Dopa@cbuu~{MeLtBgp&xkGaeH|7Y2=f~ZPjLfn`2FYqPt|K~r~Xy{ zGQB_EYy0YtiqR|n$d;BCzI)!kIKSl9x{GHbbW2=){L;;t->jV4xoGWo`{=8ETh3pd zbx(OtG9wpjLQU3|&!Egm#J>FX+IkAUFn_YAKxse z$`|_%KPleN@bk^q{qH-z+&Ay#+S7Yq_OI=~zjh1${|jv|e!I1{_S3cF+tY)GDb*f@ziRn~w;4ztzn33<{9N`#+n;XseX_D@JGo0O%hp)j_h^X@ zzuJ6D?cJ~Bh3_^zDw_LtTI97RhFaLsZ2o^T;#~5x&uu;(ZnIk`GVTAt zeaH44b8za8-QV-Nc;3Hc>2Cxx1rUF2nZGxBcfcelOGi|9Pi#oMQO_qjiA{w@jP3hJ8|{#Dj4i zyKmP|-nHT7{lh{@?+)F*`%n6I>$Vs7c-Oy{KC@-swSUTqw%jIfjOCuc{x74jM@DAe z{P^p$&MVD2J?;6u`oFKg#JmY+JCn!WKjHX}gJ0s0JYzYX`#om&%nkGBSMt>y-uHGx z-`VYESb9S($wEYex$GefqLv_n-T|cK)eHWX9X7D7aC&NC5?6{J1O;=r7MyhJKIbg1d%ftL#Ot*i zWZtb_t{3{g?9L>A*S7v=zq&HZ_Df_vy!P|i-sjaueor3e?9%%CpwR1s)}Fl99pNog zjC%PVIo`j&X{yG1oh6M5bNQ;*Ei${@wJFVOdgs@inKkp~XcQgW)w!``ky2adHEoTR z$#Y|(LV1hYF8`LSw*0sF@T+i{!?}A^uKvIF^ndqBk8@Q)IkMUNi(hTKTXybz=-1hs z%I7Kht(tI=C;WJH`;4hR-vjUQ73I5Z3`(^|;I^yg%KzXgA-zyo2{|=q=bcyR@aamNBYazTQBm z|Mw^5%^P+d|K79y>+jXk2K$b-UEUvIx9j-vYwLu+zx$eZ{klvw+nlKLE{pf9HT-hl zz2Ls{w@%SV66#-WuQ}xL@$Jcpk;}8cZCwAi*yqx`Q;mPa?_GO$Ga~!>UrG1ETh}&S zn*PvMl)7EeA8mz z#;SK}TmQlt9Cb?Po!(T6Eh~N>xPH(2uXp;&BQyeRmo6`lXW!5f`SsJ<3c2%3+jN%9 z+U{XyYNJ-2K6n56|8ifiZL8Cq)yCyyWc=_Z-&pGgVGnpPh2&> zA8hDueDBEAQwRS)e4}*Va@9qbnfAZtTnY_VbgaMK{nfTv!FX%JcW23!Pf}j_*UNc6 z__DP^o0>S#;{Wp4XD-uAq{Jo>TYS@6;??9hz<*{cy?5dgbn)>(}(n zlk=C0e{}kd*8l6Z0{Xs_X6%e#W%`IUVfq7$gH61nyBxXw_oEfzDSK-Fo)13o<2A$j{eH_&cinA2;jaJx zfBnyu^Uv?IO=j9&Y%49G?LPV63i+FDDepHPf0a;sRXzEm=f-98Z{B@#e%H0+ubcPj zAJ4n=JwDl{+`jd$@o^P(?LI%jvwTTk)SrCZ!Mpf%;+pgIkN&*-e)w&j*TG|%xd;Q||r>;%QI7Ilf1ycBO|gJUjir{Q9`rH}5sSYUK54+UJZI7GK${cK1o6@=mvh;lI8Kr`LG* zOk6neP)=LKL@|xCytm4xo&QyLcin9_jWwxfc{V$qPt$4A)m+1C_ApxH6{E#eiM#Wk zEKP0XpEqyO`QkGl?$pO@nsaOOf^Qo>*3|yzezfi7?gu=+zrQ}M5H0yz ztt*x$&Pn?|_0Z|P_cinXteRbahFL5;c(&f(n0wZ7-QRC5yZhVf-py{)o1*81L{IPd zedR9?gLQ9|ci75zI$HeI>q5->7Il^!*VQxCUX#i=oflh7j*8(4fq5R)llL1mx_n#PZN|Ccu3n^vE4WM>WM zCG35AE$fGM^83UNi8~gx zqQ?#I^e40btGzG(_4%bY3trZKxg8bJc^UMytHK&HC2w~2A zUs?;k&fnI(?K*#Xd*_a%AHVq`TdUrG;ta6Ny=pMQ-g%pFz~A}SCI4kBAKFGQTy8GN zaN&u;gSQMR+HcCM71l;&tzFNp(=1~tCR3Bj{9u*cwYD1LhSBmHPs&D-f)2|VLYjBC zrBm)&FfcIuIS~|(Wmx8(MRifV(saSF)7FtSBKPL~dEc{8-bY`X8BC+*Nx+SSLL^Pici+veLF?Q+T_4#hDjn#o)F(1D2rGtC7I*GKO<@bkN@1B zi2F0&vi9$O9qLHrgs7pnB z&XYsmd++{u6ZQ31sK!ad`}dk{?+U$|TyVp1_dfCB%D(}({zu>Y@F>NJzqIb6`5%K2 ziNn{|MYA3YdQhP+UL835^wX7kr<>n9lvL~Crr;&ckC^7vHl-Z z#N&7Cj`hCEH4N3;PjCCP^8{1Q0Z2SU0%?@Q9s>I;s#orxT)zCvweq4Qd$H{Nf8{CR zG1JNof6m#@^XAc?f0y$6e^)(F_1Jq&;`#skR$qP`UnYIe|GV{P(}V ze&g9Wy|rITiecdt$S5R3gLli{hwY!Hg?I$D-+%q6=j8Eif!2}JA7m*ct0~WoW2^kG zC>R|2FzfgIc}u3x6xCk&ceW`<_fGDu^3TP5|Loj-fiWd|#v%4J(=~iA?lP|W@#5X~ zxpN-Yy^!2t_G9(ZddtIx7dCqt8u!X_T6FP(8VEf0}RRw#4O!Qjxzpg8Y ztem##>LLZn+)389>r(IQ%jz5G9=ai3cY@!Ry`W-q9-rO!`5HZck6Fb0$?4L~^10gA zv-{|^Em|QzAGHb=O>3X%Wm%AA{rmpBBZ{h0hSjWM?#KB~=DVl-Sn1b)v`lmUd;6KQ z?i+oxEp`ayRk*|WzhqOfPjKY!HOVWofW-KqNq zykFj@+&}+v^DK_bkDngi`?LPq^BYxs)tc_#^ZxJ9?I_zX85n)H%4L33r18V>Sl975!MUc#_MY!CJz<;hcjnZs z`+oDRe*N?AVash>!}9ZdB>wNYH1CD4xbo~{br+tx=&4P%E|6AeU}{?G1D!lMV6^y2 z((()Eebr{4k5e$sK6vpe6VK-C`d^o4%DqUN5o#%rwE6x@wmC(%Q*H<@U-Ze^{J!1^ zshCG+lV>H*Y0u@?kGa#xst~c^NX}L%F0s8vauq*kt=cTK{OerRNlcAhuO6;UoLMLJ zH==QYyl2MFFx}77FRxvk5SuLGaohBHoT#x|*LkIi-`(%aO_tq|u3W5nll`7@QSaQd zUzq2|YOvSt;#yR6VpWX3{q~Y&{QnYKLNCr?%3h`{*dX;3;SYrvHWH1o*tKc zwxaH!LeiV%5A=_^sgyc}eP3iBd3}9O+xL`czsdC_ISk)yZIhq7_@8-VK4Wpty0vw| zmxJwR*-oG6X2wx4scC-7lK=kD+HGW|Qwn`+XR$lA-hKDwhYYIzyD|-|Fq%AzSfXzoYZeI?~-hEeDaxmxi~3?by^Y- zk2buq^3#dQ+;fRT`_Y!O+LoUyS6yDSrvFpV%`t3}}l z1CuxKUbEy){#Kj&>eD&3NnfqYcE0}-vRR<0w#smex~^)?V&7l7-w(X|uRfhyE86^j zaH4?Y7FGU4BeuxW-P2Ea`<7l@%jUYMTj15~v%Pz{?{#k2ku>MSL)K~Ct~$ckTvE1d zdegD5V)moO3bpw?hvFP`a|QVe<8RFRZ0YrD*XbBPVHV?iC5sQg|AzG@@3qnjI=G%?S40`-(Pxh zxlpUo{h*v?pbtxfwX`xr!`XdL9M&a3Cm$f;GD_kNfq5RgHR~tOGW-zy!BYI1+^P37 z@}{&ujm@b&CtuRO_IG}A{chPB!=8IHE?g=8UjH}p=7Eaa<{h8os*jvcwSV)+w$J8a%> z|7W@Q<@r-JnSKX0^ll74n|f4kzW4t%JN_1OoqZ=!)p6IyZQbPyhZjV?`?~q-_qo33 z4(rTH(1eZ67+h`GDWy7T*JXW)S<4@XZ%nQCEm+uA_Q+uNweS7+xelxdm>J9%!E1Tn z+x~9oYB{w9GqxF+$X$y$w?1Ds^v7)pZkgiv^L)N_tTuuE~}3nySaARA-~_*=j)ICJtH;KZ_2EyvlGMEwASm!Gi+c|-F5uWr6;Lv z=l=b<>UH28+wVRWZ$F{RxARBSHs0inOLAVs?vJz8OH(iG@2=%8v0HQZqP+**WLZy zzg!cO<)!~FeDAmK?RotB@zX!wufCVK-~U4I;%)o&vsrrX&M{kUZhL+0P30LG+kWrc z{%&!yX(j7}Q$CPQObiS%Ket@*X8FzRI8l~0(3$nblm6>v#*1R!u9&sCqqP67k(SD? zX*W1;T#f%wAaH1&`tSPfu}lA#H_BywzhJYRzc6$%_nWH*d(Z_)ranS z8;PC&c<24~sEFTde>}ZE|LDBtB8> z>hQ#;84J|QUo-^S*WLKKE5h+qb?v;)Jt;W_lPyk7nKZq+uC()OS;$l0TTh#tgH#Q} zG})SWZWOFNXY$v+Tf|=i<)c3eRr4M zdiHwdGBv>~t*!2DopUSslh`DWSN_rTGIz7PSDLC(vDSFe<+@IB0oVQiqi;vO`Bd|^ zGtVb3U3AyUuw8}`XUntRZ!Gxsz3hlxQT@x9^Svj-r4{bhIXo^qJ$P{Q zZxg=kSI?H{7u{uY%+9V);_KDxH)_6p&~!0=>ATUt zzua=s`#c9b-~6-RyxFZ@-92~h|CUE~Y18liOwPTq^tZ&E{N3qm1b7zjkMw-dWCU5{ z&fswRoU@_RvY*K>OupX#A^OQea^I}4K{knjYkwX3zUijy>M0@&XQlUV<7Q}9_I~Vl z!Rpn<-P{*rZ}Bj_|K%aI(^aTaul!-ag1Qi;Pbz$kAsOzkH^}$K)P8PWU|sC4nP;~o z!Zl}N-lXhT{7$jWy2_bHlC>VMeZIO}?EdEG)35vupC2J%>ala}gUQ7fc9~Xp%4$6Y z7PaK(dVS&C|5tg>+T1k7uS|E9OQpnrEZQ}3=C8O}FaFK`cRg{*y1uO{Pj$9`EX|$j zT)sbXYbw`o=jM8zBfT>F+0KUly!Ltcz!Q6Zv3z^p!(vclj{WdQUV_v&^Xm-xmf{7rb)$=SI{ z$4}hd^2zMy;}u_@ZhCaTZu`8~|JPkV@Z$K6yLMCLUnRfI``W0fFDmknq1gz!D!8Gz zzg_gUP2A__**E{4Tb?(|UUc8L_^Z`Ri@F4h&sW-gZAcW#C``~2@$+VXaR2$8L$#Z> zSx+%r+nRr^c=oHz;;WGhzD0`{J;>41ynI|F;N7onMZacrU)_@Co@JJL_2(S5sQFrV zjWgdpFXewyz4Tw-cOS!_M%OpIxyY@}UN4)p&HnE3R`cS0x3gSl_pX*HyMJ!pk%)v% z8znzm%t^EOcfK^g^!ag7zYfdH1t0HxPVRVrf75HvrQ8a?JomR;owSu*`|HJRL?|5?WjUT2+Zr)-F$YkuIsa#B0qPVUCKMM|DoZ8`A2Wv|H(Vy@te;P zk7dfwY`*dG_1SEfL%V+PeTqL{^UUsyw|rq$cg3ABspqGUr?+3rua~(m@cnSV=^ORm zxu>I@`Y!({uc<%&`~469i&C#Y9XFeHUFK-ft=C)U%)S4lcGK&f(%*Ld|9equ-S@9& zzDk|HmNKtuiuq%+Un-LO?;lG^&UAX^+jmE8*45diZ<4n!(&Vk|WOQToaD;7EP}sP> zbMcm{mPs>~&Hc=kc7ON1J&tBE=6m$zx9xtg`};S6?XT1him5k+EGfBM)wW|=!_@4Y z9@E%e#bP(y`ToD0_mE}j^cAa{SLSfB6|i}?-al;A|Ez592J!h`Tkm9h+`i-gRo*rE zlxUn|`(6o&_j~7Es-HH!ezW!Z*EW(I^AlxPpV_~(vElXe`&H*2pIxMGe>?Z;mRj4_ zJG;MMQ(Ah&Ec|e(>GIfhbzHA%KiSCcxv9u zZ)@Ba{dqojb)uF^>z$e>!S?^#-)(qsOFX?erOf}?0@p7!uXLUp$!cFaP#^nqUCMbi z28%Sgz=aCXERTr8Q5L}vu&da;x;|-|Kp3brk7dsGf2Y2z=gX#j-2cwyH(a|r{bcl~ z;A=OY9kiak&o6#CbA`P0pZMeV!+oNg4cz`dmoGd&p|aWH>z?v2H(#_o|MhdG{r7#- z>$lyQFTZwccca8@{@Ks`d)C>%nXu-1x?Yv>CujSMA3nb;X>Oc*dBS(stVOr~d`!KP zY-*CNo_+WDte$=15x>v>{2I4D`J3IZ8)2FaQ*}I`URXElXwZrEd6nnXn~$uxnku%= zF6P)yJ$tj$pK@O%ztgW=vdkVY z_0pqXSXADBxtl#Dc17;CyG!?cdU-Z(#k^n97vjB--&|$smjCtli(fT4IWu>ia)13M z?&amFXFOfBG`IL^*0I0dkz(7Kdw93;X5|UfB(vTm@0%zXk?xpt{&Uc;6HN;GcTc6Q zoT)LVl!hg8gF4wD|eQqD$=bBy5BQ4vb>(&c?U;8iZjo@pi zo%-mjtn$uj?aynbX50B0UN#MXc;&PxZ|M^W-{@LTk*!j*1Mi+*e((KW_wF5;T`SLh z&e7hhzW8akm%7C5yAe+g`2G3)(2mnSZ@$|S8RNO@J_LU)k^4UR^Y!?foH|5D?5INI z5P%NLBtqJKpkWy+1_p+PtGnW`49nO>?EX~m6#gLA;M&jgWt;1cFP|7UEq&uR%l)1= zvOj%U|0UzxdFf)E3zOu}{oneX`&)gukD=wCfBVhrrR49gcllrQ)&AMN+cnyWXXhzj zBH04PhZ-+ zbM56@VP=+p-yF_U@vFbN+r7Q+)f(HXH@j0O94_jwuC81wczDz78$oK≈KaygDkg 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

Ghs+xp*+S1vwfRcqI? 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 W Date: Fri, 3 May 2024 21:11:23 +0300 Subject: [PATCH 174/348] Add turret ammo capacity to the in-game help (#9777) * Add turret ammo capacity to the in-game help * Rename max shots to ammo capacity * Rename ammo multipier to ammo per item --- core/assets/bundles/bundle.properties | 4 +++- core/assets/contributors | 1 + .../mindustry/world/blocks/defense/turrets/ItemTurret.java | 1 + core/src/mindustry/world/meta/Stat.java | 1 + core/src/mindustry/world/meta/StatUnit.java | 1 + 5 files changed, 7 insertions(+), 1 deletion(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index f7aeab2413..642330901d 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -992,6 +992,7 @@ stat.abilities = Abilities stat.canboost = Can Boost stat.flying = Flying stat.ammouse = Ammo Use +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Damage Multiplier stat.healthmultiplier = Health Multiplier stat.speedmultiplier = Speed Multiplier @@ -1093,7 +1094,7 @@ bullet.pierce = [stat]{0}x[lightgray] pierce bullet.infinitepierce = [stat]pierce bullet.healpercent = [stat]{0}%[lightgray] repair bullet.healamount = [stat]{0}[lightgray] direct repair -bullet.multiplier = [stat]{0}x[lightgray] ammo multiplier +bullet.multiplier = [stat]{0}[lightgray] ammo/item bullet.reload = [stat]{0}%[lightgray] fire rate bullet.range = [stat]{0}[lightgray] tiles range @@ -1118,6 +1119,7 @@ unit.items = items unit.thousands = k unit.millions = mil unit.billions = b +unit.shots = shots unit.pershot = /shot category.purpose = Purpose category.general = General diff --git a/core/assets/contributors b/core/assets/contributors index 3b3153b7ee..c402672c93 100644 --- a/core/assets/contributors +++ b/core/assets/contributors @@ -165,3 +165,4 @@ OpalSoPL BalaM314 Redstonneur1256 ApsZoldat +hexagon-recursion diff --git a/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java b/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java index 14aa007d35..93a77b644f 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java @@ -49,6 +49,7 @@ public class ItemTurret extends Turret{ stats.remove(Stat.itemCapacity); stats.add(Stat.ammo, StatValues.ammo(ammoTypes)); + stats.add(Stat.ammoCapacity, maxAmmo / ammoPerShot, StatUnit.shots); } @Override diff --git a/core/src/mindustry/world/meta/Stat.java b/core/src/mindustry/world/meta/Stat.java index 5248a70c80..b1257d6232 100644 --- a/core/src/mindustry/world/meta/Stat.java +++ b/core/src/mindustry/world/meta/Stat.java @@ -86,6 +86,7 @@ public class Stat implements Comparable{ targetsGround = new Stat("targetsGround", StatCat.function), damage = new Stat("damage", StatCat.function), ammo = new Stat("ammo", StatCat.function), + ammoCapacity = new Stat("ammoCapacity", StatCat.function), ammoUse = new Stat("ammoUse", StatCat.function), shieldHealth = new Stat("shieldHealth", StatCat.function), cooldownTime = new Stat("cooldownTime", StatCat.function), diff --git a/core/src/mindustry/world/meta/StatUnit.java b/core/src/mindustry/world/meta/StatUnit.java index 1c2408ad74..4362c6ecdd 100644 --- a/core/src/mindustry/world/meta/StatUnit.java +++ b/core/src/mindustry/world/meta/StatUnit.java @@ -24,6 +24,7 @@ public class StatUnit{ degrees = new StatUnit("degrees"), seconds = new StatUnit("seconds"), minutes = new StatUnit("minutes"), + shots = new StatUnit("shots"), perSecond = new StatUnit("perSecond", false), perMinute = new StatUnit("perMinute", false), perShot = new StatUnit("perShot", false), From d0955f53b30a9b394d7018313b14d6116d991ec2 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Fri, 3 May 2024 18:12:14 +0000 Subject: [PATCH 175/348] Automatic bundle update --- core/assets/bundles/bundle_be.properties | 2 ++ core/assets/bundles/bundle_bg.properties | 2 ++ core/assets/bundles/bundle_ca.properties | 2 ++ core/assets/bundles/bundle_cs.properties | 2 ++ core/assets/bundles/bundle_da.properties | 2 ++ core/assets/bundles/bundle_de.properties | 2 ++ core/assets/bundles/bundle_es.properties | 2 ++ core/assets/bundles/bundle_et.properties | 2 ++ core/assets/bundles/bundle_eu.properties | 2 ++ core/assets/bundles/bundle_fi.properties | 2 ++ core/assets/bundles/bundle_fil.properties | 2 ++ core/assets/bundles/bundle_fr.properties | 2 ++ core/assets/bundles/bundle_hu.properties | 2 ++ core/assets/bundles/bundle_id_ID.properties | 2 ++ core/assets/bundles/bundle_it.properties | 2 ++ core/assets/bundles/bundle_ja.properties | 2 ++ core/assets/bundles/bundle_ko.properties | 2 ++ core/assets/bundles/bundle_lt.properties | 2 ++ core/assets/bundles/bundle_nl.properties | 2 ++ core/assets/bundles/bundle_nl_BE.properties | 2 ++ core/assets/bundles/bundle_pl.properties | 2 ++ core/assets/bundles/bundle_pt_BR.properties | 2 ++ core/assets/bundles/bundle_pt_PT.properties | 2 ++ core/assets/bundles/bundle_ro.properties | 2 ++ core/assets/bundles/bundle_ru.properties | 2 ++ core/assets/bundles/bundle_sr.properties | 2 ++ core/assets/bundles/bundle_sv.properties | 2 ++ core/assets/bundles/bundle_th.properties | 2 ++ core/assets/bundles/bundle_tk.properties | 2 ++ core/assets/bundles/bundle_tr.properties | 2 ++ core/assets/bundles/bundle_uk_UA.properties | 2 ++ core/assets/bundles/bundle_vi.properties | 2 ++ core/assets/bundles/bundle_zh_CN.properties | 2 ++ core/assets/bundles/bundle_zh_TW.properties | 2 ++ 34 files changed, 68 insertions(+) diff --git a/core/assets/bundles/bundle_be.properties b/core/assets/bundles/bundle_be.properties index ef0161806b..796e9809df 100644 --- a/core/assets/bundles/bundle_be.properties +++ b/core/assets/bundles/bundle_be.properties @@ -965,6 +965,7 @@ stat.abilities = Здольнасйі stat.canboost = Можа Узлятаць stat.flying = Паветраны stat.ammouse = Выкарыстанне Боезапасу +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Множнік Пашкоджанняў stat.healthmultiplier = Множнік Здароўя stat.speedmultiplier = Множнік Хуткасці @@ -1090,6 +1091,7 @@ unit.items = прадметаў unit.thousands = Тыс. unit.millions = М. unit.billions = Б. +unit.shots = shots unit.pershot = /стрэл category.purpose = Апісанне category.general = Асноўныя diff --git a/core/assets/bundles/bundle_bg.properties b/core/assets/bundles/bundle_bg.properties index 64a898ce26..a78369d6c1 100644 --- a/core/assets/bundles/bundle_bg.properties +++ b/core/assets/bundles/bundle_bg.properties @@ -975,6 +975,7 @@ stat.abilities = Способности stat.canboost = Може да ускорява stat.flying = Летящ stat.ammouse = Употребе на Боеприпаси +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Множител на Щети stat.healthmultiplier = Множител на Точки живот stat.speedmultiplier = Множител на Скорост @@ -1101,6 +1102,7 @@ unit.items = предмети unit.thousands = хил unit.millions = млн unit.billions = млр +unit.shots = shots unit.pershot = /изстрел category.purpose = Предназначение category.general = Обща информация diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index 8a6d9ccbb0..aaf11f4a10 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -979,6 +979,7 @@ stat.abilities = Habilitats stat.canboost = Pot sobrevolar. stat.flying = Està volant. stat.ammouse = Ús de munició +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Multiplicador de dany stat.healthmultiplier = Multiplicador de salut stat.speedmultiplier = Multiplicador de velocitat @@ -1104,6 +1105,7 @@ unit.items = elements unit.thousands = k unit.millions = M unit.billions = kM +unit.shots = shots unit.pershot = /dispar category.purpose = Funció category.general = General diff --git a/core/assets/bundles/bundle_cs.properties b/core/assets/bundles/bundle_cs.properties index eb5d899226..7ffd6977a9 100644 --- a/core/assets/bundles/bundle_cs.properties +++ b/core/assets/bundles/bundle_cs.properties @@ -977,6 +977,7 @@ stat.abilities = Schopnosti stat.canboost = Umí posilovat stat.flying = Létající stat.ammouse = Spotřeba Munice +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Násobič Poškození stat.healthmultiplier = Násobič Životů stat.speedmultiplier = Násobič Rychlostí @@ -1103,6 +1104,7 @@ unit.items = předměty unit.thousands = tis unit.millions = mio unit.billions = mld +unit.shots = shots unit.pershot = /střela category.purpose = Účel category.general = Všeobecné diff --git a/core/assets/bundles/bundle_da.properties b/core/assets/bundles/bundle_da.properties index 599894cbb9..6d0dccd596 100644 --- a/core/assets/bundles/bundle_da.properties +++ b/core/assets/bundles/bundle_da.properties @@ -966,6 +966,7 @@ stat.abilities = Evner stat.canboost = Can Boost stat.flying = Flying stat.ammouse = Ammo Use +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Damage Multiplier stat.healthmultiplier = Health Multiplier stat.speedmultiplier = Speed Multiplier @@ -1092,6 +1093,7 @@ unit.items = genstande unit.thousands = t unit.millions = mio unit.billions = mia +unit.shots = shots unit.pershot = /shot category.purpose = Purpose category.general = Generel diff --git a/core/assets/bundles/bundle_de.properties b/core/assets/bundles/bundle_de.properties index 589339ee81..d12d5bb6c3 100644 --- a/core/assets/bundles/bundle_de.properties +++ b/core/assets/bundles/bundle_de.properties @@ -988,6 +988,7 @@ stat.abilities = Fähigkeiten stat.canboost = Kann boosten stat.flying = Flug stat.ammouse = Muntionsverbrauch +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Schaden-Multiplikator stat.healthmultiplier = Lebenspunkte-Multiplikator stat.speedmultiplier = Geschwindigkeit-Multiplikator @@ -1114,6 +1115,7 @@ unit.items = Materialeinheiten unit.thousands = k unit.millions = Mio unit.billions = Mrd +unit.shots = shots unit.pershot = /Schuss category.purpose = Beschreibung category.general = Allgemeines diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index 987e4c5140..72f94349a8 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -985,6 +985,7 @@ stat.abilities = Habilidades stat.canboost = Puede volar stat.flying = Aéreo stat.ammouse = Uso de munición +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Multiplicador de daño stat.healthmultiplier = Multiplicador de vida stat.speedmultiplier = Multiplicador de velocidad @@ -1110,6 +1111,7 @@ unit.items = objetos unit.thousands = k unit.millions = M unit.billions = B +unit.shots = shots unit.pershot = /disparo category.purpose = Objetivo category.general = General diff --git a/core/assets/bundles/bundle_et.properties b/core/assets/bundles/bundle_et.properties index 9f51bdf81e..2bd2079c43 100644 --- a/core/assets/bundles/bundle_et.properties +++ b/core/assets/bundles/bundle_et.properties @@ -966,6 +966,7 @@ stat.abilities = Abilities stat.canboost = Can Boost stat.flying = Flying stat.ammouse = Ammo Use +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Damage Multiplier stat.healthmultiplier = Health Multiplier stat.speedmultiplier = Speed Multiplier @@ -1092,6 +1093,7 @@ unit.items = ressursiühikut unit.thousands = k unit.millions = mil unit.billions = b +unit.shots = shots unit.pershot = /shot category.purpose = Purpose category.general = Üldinfo diff --git a/core/assets/bundles/bundle_eu.properties b/core/assets/bundles/bundle_eu.properties index e2a0ed81fe..7cb2dcb491 100644 --- a/core/assets/bundles/bundle_eu.properties +++ b/core/assets/bundles/bundle_eu.properties @@ -968,6 +968,7 @@ stat.abilities = Abilities stat.canboost = Can Boost stat.flying = Flying stat.ammouse = Ammo Use +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Damage Multiplier stat.healthmultiplier = Health Multiplier stat.speedmultiplier = Speed Multiplier @@ -1094,6 +1095,7 @@ unit.items = elementu unit.thousands = k unit.millions = mil unit.billions = b +unit.shots = shots unit.pershot = /shot category.purpose = Purpose category.general = Orokorra diff --git a/core/assets/bundles/bundle_fi.properties b/core/assets/bundles/bundle_fi.properties index 407e7121ac..5a308f3689 100644 --- a/core/assets/bundles/bundle_fi.properties +++ b/core/assets/bundles/bundle_fi.properties @@ -965,6 +965,7 @@ stat.abilities = Erikoisvoimat stat.canboost = Voi tehostaa stat.flying = Lentävä stat.ammouse = Ammusten käyttö +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Vahinkokerroin stat.healthmultiplier = Elmäpistekerroin stat.speedmultiplier = Nopeuskerroin @@ -1091,6 +1092,7 @@ unit.items = esinettä unit.thousands = t unit.millions = milj unit.billions = mrd +unit.shots = shots unit.pershot = /laukaisu category.purpose = Tarkoitus category.general = Yleinen diff --git a/core/assets/bundles/bundle_fil.properties b/core/assets/bundles/bundle_fil.properties index 6fd0ac002a..b611eec885 100644 --- a/core/assets/bundles/bundle_fil.properties +++ b/core/assets/bundles/bundle_fil.properties @@ -965,6 +965,7 @@ stat.abilities = Abilities stat.canboost = Can Boost stat.flying = Flying stat.ammouse = Ammo Use +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Damage Multiplier stat.healthmultiplier = Health Multiplier stat.speedmultiplier = Speed Multiplier @@ -1091,6 +1092,7 @@ unit.items = items unit.thousands = k unit.millions = mil unit.billions = bil +unit.shots = shots unit.pershot = /shot category.purpose = Purpose category.general = General diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index 818c8996d3..548d329ba1 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -991,6 +991,7 @@ stat.abilities = Habilités stat.canboost = Boost stat.flying = Unité volante stat.ammouse = Utilisation de munitions +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Multiplicateur de dégâts stat.healthmultiplier = Multiplicateur de santé stat.speedmultiplier = Multiplicateur de vitesse @@ -1116,6 +1117,7 @@ unit.items = objets unit.thousands = k unit.millions = M unit.billions = Md +unit.shots = shots unit.pershot = /tirs category.purpose = Description category.general = Caractéristiques diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index 8c521530d5..80fe97b252 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -992,6 +992,7 @@ stat.abilities = Képességek stat.canboost = Erősíthető stat.flying = Repül stat.ammouse = Lőszerhasználat +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Sebzésszorzó stat.healthmultiplier = Életerőszorzó stat.speedmultiplier = Sebességszorzó @@ -1118,6 +1119,7 @@ unit.items = nyersanyag unit.thousands = k unit.millions = mil unit.billions = Mrd +unit.shots = shots unit.pershot = /lövés category.purpose = Rendeltetés category.general = Általános diff --git a/core/assets/bundles/bundle_id_ID.properties b/core/assets/bundles/bundle_id_ID.properties index d0dfa77403..bae545e043 100644 --- a/core/assets/bundles/bundle_id_ID.properties +++ b/core/assets/bundles/bundle_id_ID.properties @@ -985,6 +985,7 @@ stat.abilities = Kemampuan stat.canboost = Dapat Dipercepat stat.flying = Terbang stat.ammouse = Penggunaan Amunisi +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Penggandaan Kekuatan (dmg) stat.healthmultiplier = Penggandaan Darah stat.speedmultiplier = Penggandaan Kecepatan @@ -1110,6 +1111,7 @@ unit.items = bahan unit.thousands = rb unit.millions = jt unit.billions = m +unit.shots = shots unit.pershot = /tembakan category.purpose = Kegunaan category.general = Umum diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties index 6d11a604e9..2507cd797a 100644 --- a/core/assets/bundles/bundle_it.properties +++ b/core/assets/bundles/bundle_it.properties @@ -971,6 +971,7 @@ stat.abilities = Abilità stat.canboost = Capace di Potenziamento stat.flying = Volo stat.ammouse = Consumo di munizioni +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Moltiplicatore danni stat.healthmultiplier = Moltiplicatore salute stat.speedmultiplier = Moltiplicatore velocità @@ -1097,6 +1098,7 @@ unit.items = oggetti unit.thousands = k unit.millions = mln unit.billions = mld +unit.shots = shots unit.pershot = /colpo category.purpose = Scopo category.general = Generali diff --git a/core/assets/bundles/bundle_ja.properties b/core/assets/bundles/bundle_ja.properties index 4666e41ede..47d38095ba 100644 --- a/core/assets/bundles/bundle_ja.properties +++ b/core/assets/bundles/bundle_ja.properties @@ -977,6 +977,7 @@ stat.abilities = 能力 stat.canboost = ブースト可能 stat.flying = 飛行 stat.ammouse = 使用弾薬 +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = ダメージ倍率 stat.healthmultiplier = 体力倍率 stat.speedmultiplier = スピード倍率 @@ -1103,6 +1104,7 @@ unit.items = アイテム unit.thousands = k unit.millions = mil unit.billions = b +unit.shots = shots unit.pershot = /発 category.purpose = 説明 category.general = 一般 diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index 6b7c26a06d..4bf96659d5 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -977,6 +977,7 @@ stat.abilities = 능력 stat.canboost = 이륙 가능 stat.flying = 비행 stat.ammouse = 탄약 사용 +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = 피해량 배수 stat.healthmultiplier = 체력 배수 stat.speedmultiplier = 이동속도 배수 @@ -1102,6 +1103,7 @@ unit.items = 자원 unit.thousands = k unit.millions = m unit.billions = b +unit.shots = shots unit.pershot = /발 category.purpose = 목적 category.general = 일반 diff --git a/core/assets/bundles/bundle_lt.properties b/core/assets/bundles/bundle_lt.properties index 8769614258..5ff000217e 100644 --- a/core/assets/bundles/bundle_lt.properties +++ b/core/assets/bundles/bundle_lt.properties @@ -966,6 +966,7 @@ stat.abilities = Abilities stat.canboost = Can Boost stat.flying = Flying stat.ammouse = Ammo Use +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Damage Multiplier stat.healthmultiplier = Health Multiplier stat.speedmultiplier = Speed Multiplier @@ -1092,6 +1093,7 @@ unit.items = daiktai unit.thousands = k unit.millions = mil unit.billions = b +unit.shots = shots unit.pershot = /shot category.purpose = Purpose category.general = Bendra diff --git a/core/assets/bundles/bundle_nl.properties b/core/assets/bundles/bundle_nl.properties index 94a56a1ff3..5e3a28968d 100644 --- a/core/assets/bundles/bundle_nl.properties +++ b/core/assets/bundles/bundle_nl.properties @@ -978,6 +978,7 @@ stat.abilities = Capaciteiten stat.canboost = Kan Boosten stat.flying = Vliegende stat.ammouse = Ammunitie gebruik +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Schade Vermenigvuldiger stat.healthmultiplier = Levenspunten Vermenigvuldiger stat.speedmultiplier = Snelheids Vermenigvuldiger @@ -1104,6 +1105,7 @@ unit.items = materialen unit.thousands = k unit.millions = mln unit.billions = mjd +unit.shots = shots unit.pershot = /schot category.purpose = Doel category.general = Algemeen diff --git a/core/assets/bundles/bundle_nl_BE.properties b/core/assets/bundles/bundle_nl_BE.properties index 44e2eaf603..930991bb72 100644 --- a/core/assets/bundles/bundle_nl_BE.properties +++ b/core/assets/bundles/bundle_nl_BE.properties @@ -966,6 +966,7 @@ stat.abilities = Abilities stat.canboost = Can Boost stat.flying = Flying stat.ammouse = Ammo Use +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Damage Multiplier stat.healthmultiplier = Health Multiplier stat.speedmultiplier = Speed Multiplier @@ -1092,6 +1093,7 @@ unit.items = items unit.thousands = k unit.millions = mil unit.billions = b +unit.shots = shots unit.pershot = /shot category.purpose = Purpose category.general = General diff --git a/core/assets/bundles/bundle_pl.properties b/core/assets/bundles/bundle_pl.properties index ca8fcc8028..85e19fb74e 100644 --- a/core/assets/bundles/bundle_pl.properties +++ b/core/assets/bundles/bundle_pl.properties @@ -975,6 +975,7 @@ stat.abilities = Umiejętności stat.canboost = Może przyspieszyć stat.flying = Może latać stat.ammouse = Zużycie Amunicji +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Mnożnik Obrażeń stat.healthmultiplier = Mnożnik Zdrowia stat.speedmultiplier = Mnożnik Prędkości @@ -1101,6 +1102,7 @@ unit.items = przedmioty unit.thousands = tys. unit.millions = mln. unit.billions = mld. +unit.shots = shots unit.pershot = /strzał category.purpose = Opis category.general = Główne diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index f3327bcb00..10fe9465da 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -986,6 +986,7 @@ stat.abilities = Habilidades stat.canboost = Pode impulsionar stat.flying = Voador stat.ammouse = Consumo de Munição +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Multiplicador de Dano stat.healthmultiplier = Multiplicador de Vida stat.speedmultiplier = Multiplicador de Velocidade @@ -1111,6 +1112,7 @@ unit.items = itens unit.thousands = k unit.millions = m unit.billions = b +unit.shots = shots unit.pershot = /disparo category.purpose = Propósito category.general = Geral diff --git a/core/assets/bundles/bundle_pt_PT.properties b/core/assets/bundles/bundle_pt_PT.properties index f20c6a6a9f..5e0607e5c5 100644 --- a/core/assets/bundles/bundle_pt_PT.properties +++ b/core/assets/bundles/bundle_pt_PT.properties @@ -966,6 +966,7 @@ stat.abilities = Abilities stat.canboost = Can Boost stat.flying = Flying stat.ammouse = Ammo Use +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Damage Multiplier stat.healthmultiplier = Health Multiplier stat.speedmultiplier = Speed Multiplier @@ -1092,6 +1093,7 @@ unit.items = itens unit.thousands = k unit.millions = mil unit.billions = b +unit.shots = shots unit.pershot = /shot category.purpose = Purpose category.general = Geral diff --git a/core/assets/bundles/bundle_ro.properties b/core/assets/bundles/bundle_ro.properties index 34856c9bf1..a5ffd3722b 100644 --- a/core/assets/bundles/bundle_ro.properties +++ b/core/assets/bundles/bundle_ro.properties @@ -977,6 +977,7 @@ stat.abilities = Abilități stat.canboost = Are Propulsor stat.flying = Zboară stat.ammouse = Consum muniție +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Multiplicator Forță stat.healthmultiplier = Multiplicator Viață stat.speedmultiplier = Multiplicator Viteză @@ -1103,6 +1104,7 @@ unit.items = materiale unit.thousands = mii unit.millions = mil unit.billions = mld +unit.shots = shots unit.pershot = /lovitură category.purpose = Utilizare category.general = General diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index 39a709f1a2..0481a67066 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -978,6 +978,7 @@ stat.abilities = Способности stat.canboost = Может взлететь stat.flying = Летающий stat.ammouse = Использование боеприпасов +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Множитель урона stat.healthmultiplier = Множитель прочности stat.speedmultiplier = Множитель скорости @@ -1103,6 +1104,7 @@ unit.items = предметов unit.thousands = к unit.millions = М unit.billions = кM +unit.shots = shots unit.pershot = /выстрел category.purpose = Назначение category.general = Основные diff --git a/core/assets/bundles/bundle_sr.properties b/core/assets/bundles/bundle_sr.properties index 34f7013628..5e0870600f 100644 --- a/core/assets/bundles/bundle_sr.properties +++ b/core/assets/bundles/bundle_sr.properties @@ -979,6 +979,7 @@ stat.abilities = Spospbnosti stat.canboost = Može lebdeti stat.flying = Leteća jedinica stat.ammouse = Upotreba municije +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Umnožavač štete stat.healthmultiplier = Umnožavač izdržljivosti stat.speedmultiplier = Umnožavač brzine @@ -1105,6 +1106,7 @@ unit.items = materijali unit.thousands = hiljade unit.millions = milioni unit.billions = milijarde +unit.shots = shots unit.pershot = /pucnju category.purpose = Namena category.general = Opšte diff --git a/core/assets/bundles/bundle_sv.properties b/core/assets/bundles/bundle_sv.properties index 39f7ef5245..d35b73b11d 100644 --- a/core/assets/bundles/bundle_sv.properties +++ b/core/assets/bundles/bundle_sv.properties @@ -966,6 +966,7 @@ stat.abilities = Abilities stat.canboost = Can Boost stat.flying = Flying stat.ammouse = Ammo Use +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Damage Multiplier stat.healthmultiplier = Health Multiplier stat.speedmultiplier = Speed Multiplier @@ -1092,6 +1093,7 @@ unit.items = föremål unit.thousands = k unit.millions = mil unit.billions = b +unit.shots = shots unit.pershot = /shot category.purpose = Purpose category.general = Allmänt diff --git a/core/assets/bundles/bundle_th.properties b/core/assets/bundles/bundle_th.properties index 839e1afcc9..f24076882d 100644 --- a/core/assets/bundles/bundle_th.properties +++ b/core/assets/bundles/bundle_th.properties @@ -979,6 +979,7 @@ stat.abilities = ทักษะ stat.canboost = บูสต์ได้ stat.flying = บินได้ stat.ammouse = การใช้กระสุน +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = พหุคูณดาเมจ stat.healthmultiplier = พหุคูณพลังชีวิต stat.speedmultiplier = พหุคูณความเร็ว @@ -1104,6 +1105,7 @@ unit.items = ไอเท็ม unit.thousands = k unit.millions = mil unit.billions = b +unit.shots = shots unit.pershot = /การยิง category.purpose = วัตถุประสงค์ category.general = ทั่วไป diff --git a/core/assets/bundles/bundle_tk.properties b/core/assets/bundles/bundle_tk.properties index 7cd62351f8..413f9f4811 100644 --- a/core/assets/bundles/bundle_tk.properties +++ b/core/assets/bundles/bundle_tk.properties @@ -966,6 +966,7 @@ stat.abilities = Abilities stat.canboost = Can Boost stat.flying = Flying stat.ammouse = Ammo Use +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Damage Multiplier stat.healthmultiplier = Health Multiplier stat.speedmultiplier = Speed Multiplier @@ -1092,6 +1093,7 @@ unit.items = esya unit.thousands = k unit.millions = mil unit.billions = b +unit.shots = shots unit.pershot = /shot category.purpose = Purpose category.general = General diff --git a/core/assets/bundles/bundle_tr.properties b/core/assets/bundles/bundle_tr.properties index 927c429724..d124d89693 100644 --- a/core/assets/bundles/bundle_tr.properties +++ b/core/assets/bundles/bundle_tr.properties @@ -976,6 +976,7 @@ stat.abilities = Kabiliyetler stat.canboost = İstekli Uçabilir stat.flying = Uçuyor stat.ammouse = Mermi Kullanıyor +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Hasar Çarpanı stat.healthmultiplier = Can Çarpanı stat.speedmultiplier = Hız Çarpanı @@ -1101,6 +1102,7 @@ unit.items = eşya unit.thousands = k unit.millions = m unit.billions = b +unit.shots = shots unit.pershot = /vuruş category.purpose = Açıklama category.general = Genel diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 5922ff8742..72fbe93818 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -987,6 +987,7 @@ stat.abilities = Здібності stat.canboost = Можна прискорити stat.flying = Літає stat.ammouse = Патронів використовує +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Множник шкоди stat.healthmultiplier = Множник здоров’я stat.speedmultiplier = Множник швидкості @@ -1112,6 +1113,7 @@ unit.items = предм. unit.thousands = тис unit.millions = млн unit.billions = млрд +unit.shots = shots unit.pershot = за постріл category.purpose = Призначення category.general = Загальне diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index 6b25d60051..ca1bc0bd32 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -992,6 +992,7 @@ stat.abilities = Khả năng stat.canboost = Có thê tăng cường stat.flying = Bay stat.ammouse = Sử dụng đạn +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = Hệ số sát thương stat.healthmultiplier = Hệ số độ bền stat.speedmultiplier = Hệ số tốc độ @@ -1118,6 +1119,7 @@ unit.items = vật phẩm unit.thousands = ng unit.millions = tr unit.billions = tỷ +unit.shots = shots unit.pershot = /phát bắn category.purpose = Mục đích category.general = Chung diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index ff4c1e80f1..9553ecb6a0 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -988,6 +988,7 @@ stat.abilities = 能力 stat.canboost = 可助推 stat.flying = 空中单位 stat.ammouse = 弹药 +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = 伤害倍率 stat.healthmultiplier = 生命值倍率 stat.speedmultiplier = 移动速度倍率 @@ -1114,6 +1115,7 @@ unit.items = 物品 unit.thousands = K unit.millions = M unit.billions = B +unit.shots = shots unit.pershot = /发 category.purpose = 用途 category.general = 基础 diff --git a/core/assets/bundles/bundle_zh_TW.properties b/core/assets/bundles/bundle_zh_TW.properties index cf18d59d5d..0e4ab816b4 100644 --- a/core/assets/bundles/bundle_zh_TW.properties +++ b/core/assets/bundles/bundle_zh_TW.properties @@ -984,6 +984,7 @@ stat.abilities = 能力 stat.canboost = 推進器 stat.flying = 飛行單位 stat.ammouse = 彈藥使用 +stat.ammocapacity = Ammo Capacity stat.damagemultiplier = 傷害加成 stat.healthmultiplier = 血量加成 stat.speedmultiplier = 速度加成 @@ -1109,6 +1110,7 @@ unit.items = 物品 unit.thousands = K unit.millions = M unit.billions = B +unit.shots = shots unit.pershot = /發 category.purpose = 用途 category.general = 一般 From 179adabd56109944203eff433329f61c6ecc558e Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 3 May 2024 15:57:22 -0400 Subject: [PATCH 176/348] Fixed #9804 --- core/src/mindustry/content/Fx.java | 9 +++------ .../entities/abilities/ForceFieldAbility.java | 16 +++++++++------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index 246eec104a..1e8579f4b7 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -2434,12 +2434,9 @@ public class Fx{ shieldBreak = new Effect(40, e -> { color(e.color); stroke(3f * e.fout()); - if(e.data instanceof Unit u){ - var ab = (ForceFieldAbility)Structs.find(u.abilities, a -> a instanceof ForceFieldAbility); - if(ab != null){ - Lines.poly(e.x, e.y, ab.sides, e.rotation + e.fin(), ab.rotation); - return; - } + if(e.data instanceof ForceFieldAbility ab){ + Lines.poly(e.x, e.y, ab.sides, e.rotation + e.fin(), ab.rotation); + return; } Lines.poly(e.x, e.y, 6, e.rotation + e.fin()); diff --git a/core/src/mindustry/entities/abilities/ForceFieldAbility.java b/core/src/mindustry/entities/abilities/ForceFieldAbility.java index f391156adb..8ec26ff44a 100644 --- a/core/src/mindustry/entities/abilities/ForceFieldAbility.java +++ b/core/src/mindustry/entities/abilities/ForceFieldAbility.java @@ -32,6 +32,7 @@ public class ForceFieldAbility extends Ability{ /** State. */ protected float radiusScale, alpha; + protected boolean wasBroken; private static float realRad; private static Unit paramUnit; @@ -41,13 +42,6 @@ public class ForceFieldAbility extends Ability{ trait.absorb(); Fx.absorb.at(trait); - //break shield - if(paramUnit.shield <= trait.damage()){ - paramUnit.shield -= paramField.cooldown * paramField.regen; - - Fx.shieldBreak.at(paramUnit.x, paramUnit.y, paramField.radius, paramUnit.team.color, paramUnit); - } - paramUnit.shield -= trait.damage(); paramField.alpha = 1f; } @@ -85,6 +79,14 @@ public class ForceFieldAbility extends Ability{ @Override public void update(Unit unit){ + if(unit.shield <= 0f && !wasBroken){ + unit.shield -= cooldown * regen; + + Fx.shieldBreak.at(unit.x, unit.y, radius, unit.team.color, this); + } + + wasBroken = unit.shield <= 0f; + if(unit.shield < max){ unit.shield += Time.delta * regen; } From c2500b2a1a37f2c37d68c3ccdae678268a51652f Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 3 May 2024 21:12:49 -0400 Subject: [PATCH 177/348] arc --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 025014ab41..a8cfe6a7bc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=e812c7a008 +archash=7e559036ec From b12fac3aef9df24f4a12e69e18c9091efbe5d905 Mon Sep 17 00:00:00 2001 From: Elixias <61173114+LixieWulf@users.noreply.github.com> Date: Sat, 4 May 2024 07:18:59 -0600 Subject: [PATCH 178/348] i fix bug heheheheheheheeeeh (#9808) --- core/src/mindustry/world/blocks/environment/TallBlock.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/world/blocks/environment/TallBlock.java b/core/src/mindustry/world/blocks/environment/TallBlock.java index d2ca5bd004..f9384ff98f 100644 --- a/core/src/mindustry/world/blocks/environment/TallBlock.java +++ b/core/src/mindustry/world/blocks/environment/TallBlock.java @@ -10,6 +10,7 @@ import mindustry.world.*; public class TallBlock extends Block{ public float shadowOffset = -3f; public float layer = Layer.power + 1; + public float shadowLayer = Layer.power - 1; public float rotationRand = 20f; public float shadowAlpha = 0.6f; @@ -30,14 +31,14 @@ public class TallBlock extends Block{ public void drawBase(Tile tile){ float rot = Mathf.randomSeedRange(tile.pos() + 1, rotationRand); - Draw.z(Layer.power - 1); + Draw.z(shadowLayer); Draw.color(0f, 0f, 0f, shadowAlpha); Draw.rect(variants > 0 ? variantShadowRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantShadowRegions.length - 1))] : customShadowRegion, tile.worldx() + shadowOffset, tile.worldy() + shadowOffset, rot); Draw.color(); - Draw.z(Layer.power + 1); + Draw.z(layer); Draw.rect(variants > 0 ? variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))] : region, tile.worldx(), tile.worldy(), rot); } From c64afa925ecb66c4faa7b52c9e9eff8faf040fa8 Mon Sep 17 00:00:00 2001 From: summoner001 Date: Sat, 4 May 2024 15:21:44 +0200 Subject: [PATCH 179/348] Update bundle_hu.properties (#9807) 1) Follow changes to the English language file and translate it into Hungarian. 2) Translation into Hungarian of the quantitative adjectives that are usually omitted. --- core/assets/bundles/bundle_hu.properties | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index 80fe97b252..361d02cfa4 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -702,9 +702,9 @@ objective.destroyblock = [accent]Semmisítsd meg:\n[]{0}[lightgray]{1} objective.destroyblocks = [accent]Semmisítsd meg: [lightgray]{0}[white]/{1}\n{2}[lightgray]{3} objective.item = [accent]Termelj: [][lightgray]{0}[]/{1}\n{2}[lightgray]{3} objective.coreitem = [accent]Szállítás a támaszpontba:\n[][lightgray]{0}[]/{1}\n{2}[lightgray]{3} -objective.build = [accent]Építs: [][lightgray]{0}[]\n{1}[lightgray]{2} -objective.buildunit = [accent]Gyárts egységeket: [][lightgray]{0}[]\n{1}[lightgray]{2} -objective.destroyunits = [accent]Semmisíts meg: [][lightgray]{0}[] egységet +objective.build = [accent]Építs: [][lightgray]{0}[]db\n{1}[lightgray]{2} +objective.buildunit = [accent]Gyárts egységeket: [][lightgray]{0}[]db\n{1}[lightgray]{2} +objective.destroyunits = [accent]Semmisíts meg: [][lightgray]{0}[]db egységet objective.enemiesapproaching = [accent]Ellenség érkezik: [lightgray]{0}[] mp múlva objective.enemyescelating = [accent]Az ellenséges gyártás fokozódik: [lightgray]{0}[] mp múlva objective.enemyairunits = [accent]Az ellenséges légi egységek gyártása elkezdődik: [lightgray]{0}[] mp múlva @@ -992,7 +992,7 @@ stat.abilities = Képességek stat.canboost = Erősíthető stat.flying = Repül stat.ammouse = Lőszerhasználat -stat.ammocapacity = Ammo Capacity +stat.ammocapacity = Lőszerkapacitás stat.damagemultiplier = Sebzésszorzó stat.healthmultiplier = Életerőszorzó stat.speedmultiplier = Sebességszorzó @@ -1086,15 +1086,15 @@ bullet.armorpierce = [stat]páncéltörő bullet.maxdamagefraction = [stat]{0}%[lightgray] sebzési határérték bullet.suppression = [stat]{0} mp[lightgray] javításelnyomás ~[stat]{1}[lightgray] csempe bullet.interval = [stat]{0}/mp[lightgray] gyakoriságú lövedékek: -bullet.frags = [stat]{0}[lightgray]x repeszlövedék: -bullet.lightning = [stat]{0}[lightgray]x villámcsapás ~[stat]{1}[lightgray] sebzés +bullet.frags = [stat]{0}[lightgray]db repeszlövedék: +bullet.lightning = [stat]{0}[lightgray]db villámcsapás ~[stat]{1}[lightgray] sebzés bullet.buildingdamage = [stat]{0}%[lightgray] épületsebzés bullet.knockback = [stat]{0}[lightgray] hátralökés bullet.pierce = [stat]{0}[lightgray]x átütő erő bullet.infinitepierce = [stat]átütő erő bullet.healpercent = [stat]{0}%[lightgray] javítás bullet.healamount = [stat]{0}[lightgray] közvetlen javítás -bullet.multiplier = [stat]{0}[lightgray]x lőszerszorzó +bullet.multiplier = [stat]{0}[lightgray] lőszer/nyersanyag bullet.reload = [stat]{0}%[lightgray] tüzelési sebesség bullet.range = [stat]{0}[lightgray] csempés hatótáv @@ -1119,7 +1119,7 @@ unit.items = nyersanyag unit.thousands = k unit.millions = mil unit.billions = Mrd -unit.shots = shots +unit.shots = lövés unit.pershot = /lövés category.purpose = Rendeltetés category.general = Általános From ab002b7984a2a2198acf6995adaeebc24aec15ad Mon Sep 17 00:00:00 2001 From: NealRead <160118416+NealRead@users.noreply.github.com> Date: Sat, 4 May 2024 20:42:29 +0700 Subject: [PATCH 180/348] Update servers_v7.json (#9809) * Update servers_v7.json * Update servers_v7.json * Update servers_v7.json * Update servers_v7.json --- servers_v7.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/servers_v7.json b/servers_v7.json index 4d2295d0bf..d88b3545ed 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -289,6 +289,10 @@ "name": "MineCore", "address": ["194.247.42.11:27792", "194.247.42.11:27977", "194.247.42.61:27989", "194.247.42.181:28514"] }, + { + "name": "Neither", + "address": ["178.20.45.59"] + }, { "name": "Mindus•VN", "address": ["alpha.ateex.cloud:19172"] From ee1072883438fbf9d05d5921b3840b874a52ff07 Mon Sep 17 00:00:00 2001 From: GaviTSRA <61122293+GaviTSRA@users.noreply.github.com> Date: Sat, 4 May 2024 17:29:38 +0200 Subject: [PATCH 181/348] Update servers_v7.json - Add TSR Custom Server (#9810) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index d88b3545ed..9ed1c73c76 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -1,7 +1,7 @@ [ { "name": "TSR Network", - "address": ["de-prem-01.hosts.optikservers.com:35526", "de-prem-01.hosts.optikservers.com:35915", "de-prem-01.hosts.optikservers.com:35250", "de-prem-01.hosts.optikservers.com:27526", "de-prem-01.hosts.optikservers.com:35376"] + "address": ["de-prem-01.hosts.optikservers.com:35526", "de-prem-01.hosts.optikservers.com:35915", "de-prem-01.hosts.optikservers.com:35250", "de-prem-01.hosts.optikservers.com:27526", "de-prem-01.hosts.optikservers.com:35376", "de-prem-01.hosts.optikservers.com:35895"] }, { "name": "Toast Mindustry", From afdf77427832d2bde693df0a7f09026778304401 Mon Sep 17 00:00:00 2001 From: NealRead <160118416+NealRead@users.noreply.github.com> Date: Mon, 6 May 2024 00:49:57 +0700 Subject: [PATCH 182/348] Update servers_v7.json (#9814) * Update servers_v7.json * Update servers_v7.json * Update servers_v7.json * Update servers_v7.json * Update servers_v7.json --- servers_v7.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/servers_v7.json b/servers_v7.json index 9ed1c73c76..faf440da65 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -289,12 +289,12 @@ "name": "MineCore", "address": ["194.247.42.11:27792", "194.247.42.11:27977", "194.247.42.61:27989", "194.247.42.181:28514"] }, - { - "name": "Neither", - "address": ["178.20.45.59"] - }, { "name": "Mindus•VN", "address": ["alpha.ateex.cloud:19172"] + }, + { + "name": "Impact", + "address": ["impactmindustry.ddns.net"] } ] From fd81bf13dd6a2d16b64215c01a570c8bc2d7ad89 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 6 May 2024 17:11:00 -0400 Subject: [PATCH 183/348] Closes Anuken/Mindustry-Suggestions/issues/4993 --- core/src/mindustry/content/Bullets.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/content/Bullets.java b/core/src/mindustry/content/Bullets.java index 0a42bda43d..ea10110ae8 100644 --- a/core/src/mindustry/content/Bullets.java +++ b/core/src/mindustry/content/Bullets.java @@ -37,7 +37,9 @@ public class Bullets{ damageLightningGround = damageLightning.copy(); damageLightningGround.collidesAir = false; - fireball = new FireBulletType(1f, 4); + fireball = new FireBulletType(1f, 4){{ + hittable = false; + }}; spaceLiquid = new SpaceLiquidBulletType(){{ knockback = 0.7f; From d21d60627ec82a3be6226ed3d440159057e2fee3 Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 7 May 2024 07:59:02 -0400 Subject: [PATCH 184/348] Fixed #9820 --- core/assets/logicids.dat | Bin 4680 -> 4650 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/core/assets/logicids.dat b/core/assets/logicids.dat index 1a821f841ab622df099a5fbfb7d540afb380f134..26e11eb4c2686292106e65bec8aa482e3bfad74b 100644 GIT binary patch delta 40 wcmX@1vPxwGt01GuWH!N7VjLx Date: Tue, 7 May 2024 11:39:11 -0400 Subject: [PATCH 185/348] Fixed #9821 --- core/src/mindustry/world/blocks/defense/turrets/Turret.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/mindustry/world/blocks/defense/turrets/Turret.java b/core/src/mindustry/world/blocks/defense/turrets/Turret.java index a2cbbfc178..3245ecc6e1 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/Turret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/Turret.java @@ -137,6 +137,7 @@ public class Turret extends ReloadTurret{ liquidCapacity = 20f; quickRotate = false; outlinedIcon = 1; + drawLiquidLight = false; } @Override From 251e5cc3e750f15060f113e68b6a1a1ced699dd0 Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 7 May 2024 13:31:39 -0400 Subject: [PATCH 186/348] Pathfinder error fix --- core/src/mindustry/ai/ControlPathfinder.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/ai/ControlPathfinder.java b/core/src/mindustry/ai/ControlPathfinder.java index f5ad811409..978fa525b6 100644 --- a/core/src/mindustry/ai/ControlPathfinder.java +++ b/core/src/mindustry/ai/ControlPathfinder.java @@ -455,7 +455,9 @@ public class ControlPathfinder implements Runnable{ }else{ //reset data for(var p : cluster.portals){ - p.clear(); + if(p != null){ + p.clear(); + } } } From a8b08dadea83902ce757545dcdba8cba9b342424 Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 7 May 2024 15:26:32 -0400 Subject: [PATCH 187/348] Pathfinder crash "fix" --- core/src/mindustry/ai/ControlPathfinder.java | 36 ++++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/core/src/mindustry/ai/ControlPathfinder.java b/core/src/mindustry/ai/ControlPathfinder.java index 978fa525b6..9cd88c4ac0 100644 --- a/core/src/mindustry/ai/ControlPathfinder.java +++ b/core/src/mindustry/ai/ControlPathfinder.java @@ -963,9 +963,9 @@ public class ControlPathfinder implements Runnable{ for(int dir = 0; dir < 4; dir++){ int ox = cx + nextOffsets[dir * 2], oy = cy + nextOffsets[dir * 2 + 1]; - if(ox < 0 || oy < 0 || ox >= cwidth || ox >= cheight) continue; + if(ox < 0 || oy < 0 || ox >= cwidth || oy >= cheight) continue; - var otherField = cache.fields.get(ox + oy * cwidth); + var otherField = fields.get(ox + oy * cwidth); if(otherField == null) continue; @@ -1140,6 +1140,8 @@ public class ControlPathfinder implements Runnable{ if(fieldCache != null && tileOn != null){ FieldCache old = request.oldCache; + FieldCache targetCache = old != null ? old : fieldCache; + boolean requeue = old == null; //nullify the old field to be GCed, as it cannot be relevant anymore (this path is complete) if(fieldCache.frontier.isEmpty() && old != null){ request.oldCache = null; @@ -1156,7 +1158,7 @@ public class ControlPathfinder implements Runnable{ //find the next tile until one near a solid block is discovered while(i ++ < maxIterations){ - int value = getCost(fieldCache, old, tileOn.x, tileOn.y); + int value = getCost(targetCache, tileOn.x, tileOn.y, requeue); Tile current = null; int minCost = 0; @@ -1169,7 +1171,7 @@ public class ControlPathfinder implements Runnable{ if(other == null) continue; int packed = world.packArray(dx, dy); - int otherCost = getCost(fieldCache, old, dx, dy), relCost = otherCost - value; + int otherCost = getCost(targetCache, dx, dy, requeue), relCost = otherCost - value; if(relCost > 2 || otherCost <= 0){ anyNearSolid = true; @@ -1252,23 +1254,21 @@ public class ControlPathfinder implements Runnable{ initializePathRequest(request, request.team, request.costId, request.unit.tileX(), request.unit.tileY(), request.destination % wwidth, request.destination / wwidth); } - private int getCost(FieldCache cache, FieldCache old, int x, int y){ - //fall back to the old flowfield when possible - it's best not to use partial results from the base cache - if(old != null){ - return getCost(old, x, y, false); - } - return getCost(cache, x, y, true); - } - private int getCost(FieldCache cache, int x, int y, boolean requeue){ - int[] field = cache.fields.get(x / clusterSize + (y / clusterSize) * cwidth); - if(field == null){ - if(!requeue) return 0; - //request a new flow cluster if one wasn't found; this may be a spammed a bit, but the function will return early once it's created the first time - queue.post(() -> addFlowCluster(cache, x / clusterSize, y / clusterSize, true)); + try{ + int[] field = cache.fields.get(x / clusterSize + (y / clusterSize) * cwidth); + if(field == null){ + if(!requeue) return 0; + //request a new flow cluster if one wasn't found; this may be a spammed a bit, but the function will return early once it's created the first time + queue.post(() -> addFlowCluster(cache, x / clusterSize, y / clusterSize, true)); + return 0; + } + return field[(x % clusterSize) + (y % clusterSize) * clusterSize]; + }catch(ArrayIndexOutOfBoundsException e){ + //TODO: this crashes because the fields are being added while they're accessed. really bad. needs a long-term solution and some way to cache the map lookup results. + //using an array instead of a map would be nice, but that can mean something like 2500 entries in a sparse array, which is pretty terrible... return 0; } - return field[(x % clusterSize) + (y % clusterSize) * clusterSize]; } private static boolean raycast(int team, PathCost type, int x1, int y1, int x2, int y2){ From db314c1134f31f3e4df6d8bd0cbe01021e1a7555 Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 7 May 2024 16:33:08 -0400 Subject: [PATCH 188/348] Possible pathfinder error fix --- core/src/mindustry/ai/ControlPathfinder.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/mindustry/ai/ControlPathfinder.java b/core/src/mindustry/ai/ControlPathfinder.java index 9cd88c4ac0..b3b647dcee 100644 --- a/core/src/mindustry/ai/ControlPathfinder.java +++ b/core/src/mindustry/ai/ControlPathfinder.java @@ -817,6 +817,10 @@ public class ControlPathfinder implements Runnable{ int cluster = NodeIndex.cluster(current), dir = NodeIndex.dir(current), portal = NodeIndex.portal(current); int cx = cluster % cwidth, cy = cluster / cwidth; + + //invalid cluster index (TODO: how?) + if(cx >= cwidth || cy >= cheight || cx < 0 || cy < 0) continue; + Cluster clust = getCreateCluster(team, pathCost, cluster); LongSeq innerCons = clust.portalConnections[dir] == null || portal >= clust.portalConnections[dir].length ? null : clust.portalConnections[dir][portal]; From 7d2f3354af5114abb14a77c3bde337cde9b47379 Mon Sep 17 00:00:00 2001 From: Ayushkiller <152204318+Ayushkiller@users.noreply.github.com> Date: Wed, 8 May 2024 19:30:05 +0530 Subject: [PATCH 189/348] Update servers_v7.json (#9825) * Update servers_v7.json Time to expand from YouTube to servers . * Update servers_v7.json --------- Co-authored-by: Anuken --- servers_v7.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index faf440da65..86b92a98c4 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -18,7 +18,6 @@ { "name": "Meow Empire ≫ MEM PROJECT", "address": ["a2.corenodes.host:25514"] - }, { "name" : "LesGarsCools", @@ -28,6 +27,10 @@ "name": "meiqiuMDT", "address": ["cn1.meiqiumdt.top","cn1.meiqiumdt.top:7000","cn1.meiqiumdt.top:8000","cn1.meiqiumdt.top:7013","cn1.meiqiumdt.top:9000"] }, + { + "name": "DontVin", + "address": ["alpha.embotic.xyz:1316","alpha.embotic.xyz:1355","alpha.embotic.xyz:1384","alpha.embotic.xyz:1383","be.phoenix-network.dev:4200","be.phoenix-network.dev:6942","alpha.embotic.xyz:1327","alpha.embotic.xyz:1310","alpha.embotic.xyz:1382","alpha.embotic.xyz:1387","alpha.embotic.xyz:1386","alpha.embotic.xyz:1385"] + }, { "name": "Crux's Citadel", "address": ["45.158.9.198:32865","45.158.9.198:30055","45.158.9.198:32175","45.158.9.198:30252","176.9.150.40:35899","129.154.47.57:26645","176.9.150.40:35930","129.154.47.57:27993","129.154.47.57:26881","176.9.150.40:35168","176.9.150.40:35900","176.9.150.40:35031","176.9.150.40:35154"] From 6cf371b9494336a302a274144b4006e74e321acd Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 8 May 2024 10:24:38 -0400 Subject: [PATCH 190/348] Unloader optimizations from buthed --- .../world/blocks/storage/Unloader.java | 69 ++++++++++--------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/core/src/mindustry/world/blocks/storage/Unloader.java b/core/src/mindustry/world/blocks/storage/Unloader.java index a707676222..db0fdbfe1e 100644 --- a/core/src/mindustry/world/blocks/storage/Unloader.java +++ b/core/src/mindustry/world/blocks/storage/Unloader.java @@ -24,6 +24,9 @@ public class Unloader extends Block{ public float speed = 1f; + /** Cached result of content.items() */ + static Item[] allItems; + public Unloader(String name){ super(name); update = true; @@ -41,6 +44,13 @@ public class Unloader extends Block{ configClear((UnloaderBuild tile) -> tile.sortItem = null); } + @Override + public void init(){ + super.init(); + + allItems = content.items().toArray(Item.class); + } + @Override public void setStats(){ super.setStats(); @@ -63,27 +73,17 @@ public class Unloader extends Block{ float loadFactor; boolean canLoad; boolean canUnload; + /** Cached !(building instanceof StorageBuild) */ + boolean notStorage; int lastUsed; - - @Override - public String toString(){ - return "ContainerStat{" + - "building=" + building.block + "#" + building.id + - ", loadFactor=" + loadFactor + - ", canLoad=" + canLoad + - ", canUnload=" + canUnload + - ", lastUsed=" + lastUsed + - '}'; - } } public class UnloaderBuild extends Building{ public float unloadTimer = 0f; public int rotations = 0; - private final int itemsLength = content.items().size; public Item sortItem = null; public ContainerStat dumpingFrom, dumpingTo; - public final Seq possibleBlocks = new Seq<>(); + public final Seq possibleBlocks = new Seq<>(ContainerStat.class); protected final Comparator comparator = (x, y) -> { //sort so it gives priority for blocks that can only either receive or give (not both), and then by load, and then by last use @@ -99,19 +99,20 @@ public class Unloader extends Block{ private boolean isPossibleItem(Item item){ boolean hasProvider = false, - hasReceiver = false, - isDistinct = false; + hasReceiver = false, + isDistinct = false; - for(int i = 0; i < possibleBlocks.size; i++){ - var pb = possibleBlocks.get(i); + var pbi = possibleBlocks.items; + for(int i = 0, l = possibleBlocks.size; i < l; i++){ + var pb = pbi[i]; var other = pb.building; //set the stats of buildings in possibleBlocks while we are at it - pb.canLoad = !(other.block instanceof StorageBlock) && other.acceptItem(this, item); + pb.canLoad = pb.notStorage && other.acceptItem(this, item); pb.canUnload = other.canUnload() && other.items != null && other.items.has(item); //thats also handling framerate issues and slow conveyor belts, to avoid skipping items if nulloader - if((hasProvider && pb.canLoad) || (hasReceiver && pb.canUnload)) isDistinct = true; + isDistinct |= (hasProvider && pb.canLoad) || (hasReceiver && pb.canUnload); hasProvider |= pb.canUnload; hasReceiver |= pb.canLoad; } @@ -129,14 +130,15 @@ public class Unloader extends Block{ for(int i = 0; i < proximity.size; i++){ var other = proximity.get(i); if(!other.interactable(team)) continue; //avoid blocks of the wrong team - ContainerStat pb = Pools.obtain(ContainerStat.class, ContainerStat::new); //partial check boolean canLoad = !(other.block instanceof StorageBlock); boolean canUnload = other.canUnload() && other.items != null; if(canLoad || canUnload){ //avoid blocks that can neither give nor receive items + var pb = Pools.obtain(ContainerStat.class, ContainerStat::new); pb.building = other; + pb.notStorage = canLoad; //TODO store the partial canLoad/canUnload? possibleBlocks.add(pb); } @@ -154,9 +156,9 @@ public class Unloader extends Block{ }else{ //selects the next item for nulloaders //inspired of nextIndex() but for all "proximity" (possibleBlocks) at once, and also way more powerful - for(int i = 0; i < itemsLength; i++){ - int total = (rotations + i + 1) % itemsLength; - Item possibleItem = content.item(total); + for(int i = 0, l = allItems.length; i < l; i++){ + int id = (rotations + i + 1) % l; + var possibleItem = allItems[id]; if(isPossibleItem(possibleItem)){ item = possibleItem; @@ -167,11 +169,14 @@ public class Unloader extends Block{ if(item != null){ rotations = item.id; //next rotation for nulloaders //TODO maybe if(sortItem == null) + var pbi = possibleBlocks.items; + int pbs = possibleBlocks.size; - for(int i = 0; i < possibleBlocks.size; i++){ - var pb = possibleBlocks.get(i); + for(int i = 0; i < pbs; i++){ + var pb = pbi[i]; var other = pb.building; - pb.loadFactor = (other.getMaximumAccepted(item) == 0) || (other.items == null) ? 0 : other.items.get(item) / (float)other.getMaximumAccepted(item); + int maxAccepted = other.getMaximumAccepted(item); + pb.loadFactor = maxAccepted == 0 || other.items == null ? 0 : other.items.get(item) / (float)maxAccepted; pb.lastUsed = (pb.lastUsed + 1) % Integer.MAX_VALUE; //increment the priority if not used } @@ -181,17 +186,17 @@ public class Unloader extends Block{ dumpingFrom = null; //choose the building to accept the item - for(int i = 0; i < possibleBlocks.size; i++){ - if(possibleBlocks.get(i).canLoad){ - dumpingTo = possibleBlocks.get(i); + for(int i = 0; i < pbs; i++){ + if(pbi[i].canLoad){ + dumpingTo = pbi[i]; break; } } //choose the building to take the item from - for(int i = possibleBlocks.size - 1; i >= 0; i--){ - if(possibleBlocks.get(i).canUnload){ - dumpingFrom = possibleBlocks.get(i); + for(int i = pbs - 1; i >= 0; i--){ + if(pbi[i].canUnload){ + dumpingFrom = pbi[i]; break; } } From bd610b6925f265c50244c5852051c70ce04f1d6f Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 8 May 2024 20:15:14 -0400 Subject: [PATCH 191/348] Request a GL 3 context by default --- android/src/mindustry/android/AndroidLauncher.java | 1 + desktop/src/mindustry/desktop/DesktopLauncher.java | 4 ++++ ios/src/mindustry/ios/IOSLauncher.java | 4 +++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/android/src/mindustry/android/AndroidLauncher.java b/android/src/mindustry/android/AndroidLauncher.java index 7febad64a7..fdb8a4ec56 100644 --- a/android/src/mindustry/android/AndroidLauncher.java +++ b/android/src/mindustry/android/AndroidLauncher.java @@ -184,6 +184,7 @@ public class AndroidLauncher extends AndroidApplication{ }, new AndroidApplicationConfiguration(){{ useImmersiveMode = true; hideStatusBar = true; + useGL30 = true; }}); checkFiles(getIntent()); diff --git a/desktop/src/mindustry/desktop/DesktopLauncher.java b/desktop/src/mindustry/desktop/DesktopLauncher.java index 250d7e0a01..44f3b5b03f 100644 --- a/desktop/src/mindustry/desktop/DesktopLauncher.java +++ b/desktop/src/mindustry/desktop/DesktopLauncher.java @@ -41,6 +41,9 @@ public class DesktopLauncher extends ClientLauncher{ maximized = true; width = 900; height = 700; + //request 3.1, which has instancing + gl30Minor = 1; + gl30 = true; for(int i = 0; i < arg.length; i++){ if(arg[i].charAt(0) == '-'){ String name = arg[i].substring(1); @@ -49,6 +52,7 @@ public class DesktopLauncher extends ClientLauncher{ case "width": width = Integer.parseInt(arg[i + 1]); break; case "height": height = Integer.parseInt(arg[i + 1]); break; case "gl3": gl30 = true; break; + case "gl2": gl30 = false; break; case "antialias": samples = 16; break; case "debug": Log.level = LogLevel.debug; break; case "maximized": maximized = Boolean.parseBoolean(arg[i + 1]); break; diff --git a/ios/src/mindustry/ios/IOSLauncher.java b/ios/src/mindustry/ios/IOSLauncher.java index f6a2834b9f..cf1b6b8208 100644 --- a/ios/src/mindustry/ios/IOSLauncher.java +++ b/ios/src/mindustry/ios/IOSLauncher.java @@ -174,7 +174,9 @@ public class IOSLauncher extends IOSApplication.Delegate{ forced = false; UINavigationController.attemptRotationToDeviceOrientation(); } - }, new IOSApplicationConfiguration()); + }, new IOSApplicationConfiguration(){{ + useGL30 = true; + }}); } @Override From 2a994a2637135971ecf38e0991abff9bedb59393 Mon Sep 17 00:00:00 2001 From: HubsvVN <157294903+HubsvVN@users.noreply.github.com> Date: Thu, 9 May 2024 20:13:39 +0700 Subject: [PATCH 192/348] Update servers_v7.json (#9829) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 86b92a98c4..45f04ae3fe 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -294,7 +294,7 @@ }, { "name": "Mindus•VN", - "address": ["alpha.ateex.cloud:19172"] + "address": ["167.71.199.155:25550"] }, { "name": "Impact", From 328ba21cebded9ad8f1dc24b10c91baf49096fad Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 11 May 2024 13:35:26 -0400 Subject: [PATCH 193/348] Fixed trail not shortening --- core/src/mindustry/graphics/Trail.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/mindustry/graphics/Trail.java b/core/src/mindustry/graphics/Trail.java index f77a31fd31..6fef429cb2 100644 --- a/core/src/mindustry/graphics/Trail.java +++ b/core/src/mindustry/graphics/Trail.java @@ -106,9 +106,9 @@ public class Trail{ int count = (int)(counter += Time.delta); counter -= count; - if(points.size + ((count - 1) * 3) > length * 3 && points.size > 0){ - points.removeRange(0, Math.min(3 * count - 1, points.size - 1)); - } + if(count > 0 && points.size > 0){ + points.removeRange(0, Math.min(count * 3 - 1, points.size - 1)); + } } /** Adds a new point to the trail at intervals. */ From 62266a0652ab562a660113cbfd123f4ba47cf4d6 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 12 May 2024 09:22:00 -0400 Subject: [PATCH 194/348] Update bug_report.yml --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 3a649b83de..d630fd5965 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -72,3 +72,5 @@ body: required: true - label: I have searched the closed and open issues to make sure that this problem has not already been reported. required: true + - label: "I am **not** using Foo's Client, and have made sure the bug is not caused by mods I have installed." + required: true From 5cc48125bbdd7e376eff6165d04db7bed81d353c Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 12 May 2024 09:23:01 -0400 Subject: [PATCH 195/348] Update bug_report.yml --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index d630fd5965..53c3710077 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -72,5 +72,5 @@ body: required: true - label: I have searched the closed and open issues to make sure that this problem has not already been reported. required: true - - label: "I am **not** using Foo's Client, and have made sure the bug is not caused by mods I have installed." + - label: "I am not using Foo's Client, and have made sure the bug is not caused by mods I have installed." required: true From 515431de02a3259e86c1d9d1800d89a3dbecfbc6 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 13 May 2024 10:24:26 -0400 Subject: [PATCH 196/348] Fixed #9847 --- core/src/mindustry/ui/dialogs/PlanetDialog.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index 0345b04775..0c43dbff42 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -998,6 +998,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ sector.save = null; } updateSelected(); + rebuildList(); }); } From 7074ceaf4f032f3c60edd7ec4dc1c7ad97535563 Mon Sep 17 00:00:00 2001 From: Buj <42136194+5GameMaker@users.noreply.github.com> Date: Mon, 13 May 2024 21:30:42 +0700 Subject: [PATCH 197/348] We have a domain now (`mindurka.fun`) (#9846) Changed `MinDurka` to use the official domain (as well as added a new server) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 45f04ae3fe..857fb06ed4 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -105,7 +105,7 @@ }, { "name": "MinDurka", - "address": ["45.87.104.124", "45.87.104.124:3001", "45.87.104.124:3002", "45.87.104.124:3003", "45.87.104.124:3004", "45.87.104.124:3005", "45.87.104.124:3006", "45.87.104.124:3007", "45.87.104.124:3008", "45.87.104.124:3009", "45.87.104.124:3010"] + "address": ["mindurka.fun", "mindurka.fun:3001", "mindurka.fun:3002", "mindurka.fun:3003", "mindurka.fun:3004", "mindurka.fun:3005", "mindurka.fun:3006", "mindurka.fun:3007", "mindurka.fun:3008", "mindurka.fun:3009", "mindurka.fun:3010", "mindurka.fun:3011"] }, { "name": "Chaotic Neutral", From a04ca2203ec2b7287db94736ad6aa27c642e484a Mon Sep 17 00:00:00 2001 From: Leo <86385005+Leo-MathGuy@users.noreply.github.com> Date: Mon, 13 May 2024 22:53:21 +0500 Subject: [PATCH 198/348] Update servers_v7.json (#9849) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 857fb06ed4..9ba1496bf8 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -29,7 +29,7 @@ }, { "name": "DontVin", - "address": ["alpha.embotic.xyz:1316","alpha.embotic.xyz:1355","alpha.embotic.xyz:1384","alpha.embotic.xyz:1383","be.phoenix-network.dev:4200","be.phoenix-network.dev:6942","alpha.embotic.xyz:1327","alpha.embotic.xyz:1310","alpha.embotic.xyz:1382","alpha.embotic.xyz:1387","alpha.embotic.xyz:1386","alpha.embotic.xyz:1385"] + "address": ["51.89.173.50:1316","51.89.173.50:1355","51.89.173.50:1384","51.89.173.50:1383","be.phoenix-network.dev:4200","be.phoenix-network.dev:6942","51.89.173.50:1327","51.89.173.50:1310","51.89.173.50:1382","51.89.173.50:1387","51.89.173.50:1386","51.89.173.50:1385"] }, { "name": "Crux's Citadel", From 8fca7aa832ddc8bd6edb8ad94bca43981ef08e65 Mon Sep 17 00:00:00 2001 From: Nautilus <57863175+TomtheCoder2@users.noreply.github.com> Date: Mon, 13 May 2024 23:23:27 +0200 Subject: [PATCH 199/348] Update servers_v7.json (#9850) removing Phoenix Network, as we merged with DontVin --- servers_v7.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/servers_v7.json b/servers_v7.json index 9ba1496bf8..b7473e4b70 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -119,10 +119,6 @@ "name": "Korea", "address": ["mindustry.kr"] }, - { - "name": "Phoenix Network", - "address": ["phoenix-network.dev", "phoenix-network.dev:6464", "phoenix-network.dev:2023"] - }, { "name": "LatamDustry", "address": ["45.158.9.23:5532", "45.158.9.23:5422", "45.158.9.23:5463", "45.158.9.23:5520"] From 57d9099d9c9cc5ab8e2cec93728a846e159d7ecf Mon Sep 17 00:00:00 2001 From: abcxyzDustry <138785336+abcxyzDustry@users.noreply.github.com> Date: Wed, 15 May 2024 20:40:34 +0700 Subject: [PATCH 200/348] Update servers_v7.json (#9853) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index b7473e4b70..6a03d7a0ec 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -242,7 +242,7 @@ }, { "name": "abcxyz remaster", - "address": ["157.90.4.54:25421", "144.76.57.59:21722", "51.81.166.66:29985", "144.76.57.59:24669", "23.88.73.88:15067", "23.88.73.88:15223", "144.76.57.59:25865", "23.88.73.88:15209"] + "address": ["144.76.57.59:30302"] }, { "name": "CroCraft Network", From a14daab7e98361282e7fb5d970497f6feb279e60 Mon Sep 17 00:00:00 2001 From: WayZer Date: Sun, 19 May 2024 02:25:41 +0800 Subject: [PATCH 201/348] Update BeamDrill.java (#9857) --- core/src/mindustry/world/blocks/production/BeamDrill.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/world/blocks/production/BeamDrill.java b/core/src/mindustry/world/blocks/production/BeamDrill.java index 21207b3cda..163019c9c2 100644 --- a/core/src/mindustry/world/blocks/production/BeamDrill.java +++ b/core/src/mindustry/world/blocks/production/BeamDrill.java @@ -265,7 +265,7 @@ public class BeamDrill extends Block{ @Override public boolean shouldConsume(){ - return items.total() < itemCapacity && lastItem != null && enabled; + return items.total() < itemCapacity && facingAmount > 0 && enabled; } @Override From 82cffef09b0a948174087c224883d3a09cc24cd7 Mon Sep 17 00:00:00 2001 From: summoner001 Date: Sun, 19 May 2024 15:21:30 +0200 Subject: [PATCH 202/348] Update bundle_hu.properties (#9859) Rapairing the translation of the Tech tree and revision of the Research button text. Sorry for my mistake! :( --- core/assets/bundles/bundle_hu.properties | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index 361d02cfa4..e8bd332dbc 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -198,14 +198,14 @@ campaign.none = [lightgray]Válassz egy bolygót a kezdéshez.\nEzt bármikor me campaign.erekir = Újabb, csiszoltabb tartalom. Többnyire lineáris játékmenet.\n\nSokkal nehezebb. Magasabb minőségű pályák és élmények. campaign.serpulo = Régebbi tartalom. A klasszikus élmény. Nyíltabb végű, több tartalommal.\n\nPotenciálisan kiegyensúlyozatlan pályák és hadjárat. Kevésbé csiszolt. completed = [accent]Kész -techtree = Fejlesztési fa -techtree.select = Fejlesztési fa kiválasztása +techtree = Technológia fa +techtree.select = Technológia fa kiválasztása techtree.serpulo = Serpulo techtree.erekir = Erekir research.load = Betöltés research.discard = Eldobás research.list = [lightgray]Fejleszd ki: -research = Fejlesztési fa +research = Fejlesztés researched = [lightgray]{0} kifejlesztve. research.progress = {0}% kész players = {0} játékos @@ -1903,8 +1903,8 @@ hint.breaking = [accent]Jobb egérgombbal[] és húzással lebonthatod a blokkok hint.breaking.mobile = Használd a jobb alsó sarokban lévő \ue817 [accent]kalapács[] gombot a blokkok törléséhez.\n\nTartsd lenyomva az ujjad és húzd, hogy nagyobb területet tudj kijelölni. hint.blockInfo = Egy blokk információinak megtekintéséhez válaszd ki az épületet az [accent]építési menüben[], majd válaszd a [accent][[?][] gomb jobb oldalt. hint.derelict = Az [accent]elhagyatott[] szerkezetek régi bázisok maradványai, amelyek már nem működnek.\n\nEzeket az épületeket le lehet [accent]bontani[] nyersanyagokért, vagy meg is lehet javítani őket. -hint.research = Használd a \ue875 [accent]Fejlesztési fa[] gombot, hogy új technológiákat fedezz fel. -hint.research.mobile = Használd a \ue875 [accent]Fejlesztési fa[] gombot a \ue88c [accent]menüben[], hogy új technológiákat fedezz fel. +hint.research = Használd a \ue875 [accent]Technológia fa[] gombot, hogy új technológiákat fedezz fel. +hint.research.mobile = Használd a \ue875 [accent]Technológia fa[] gombot a \ue88c [accent]menüben[], hogy új technológiákat fedezz fel. hint.unitControl = Nyomd le a [accent][[bal Ctrl][] gombot, és kattints [accent]jobb egérgombbal[] a baráti egység vagy lövegtorony irányításához. hint.unitControl.mobile = [accent][[Dupla koppintással][] a szövetséges egységek vagy lövegtornyok kézileg irányíthatóak. hint.unitSelectControl = Az egységek irányításához lépj be [accent]parancs módba[] a [accent]bal Shift[] lenyomva tartásával.\nParancs módban az egységek kijelöléséhez kattints, és húzd az egeret. A [accent]jobb egérgombbal[] küldd az egységeket a helyszínre vagy a célponthoz. @@ -1933,8 +1933,8 @@ hint.factoryControl.mobile = Egy egységgyár [accent]kimeneti célpontjának[] gz.mine = Menj a földön lévő \uf8c4 [accent]rézérc[] közelébe, és kattints a bányászat megkezdéséhez. gz.mine.mobile = Menj a földön lévő \uf8c4 [accent]rézérc[] közelébe, és koppints a bányászat megkezdéséhez. -gz.research = Nyisd meg a \ue875 Fejlesztési fát.\nFejleszd ki a \uf870 [accent]Mechanikus fúrót[], majd válaszd ki a jobb alsó sarokban lévő menüből.\nKattints egy rézfoltra az elhelyezéséhez. -gz.research.mobile = Nyisd meg a \ue875 Fejlesztési fát.\nFejleszd ki a \uf870 [accent]Mechanikus fúrót[], majd válaszd ki a jobb alsó sarokban lévő menüből.\nKattints egy rézfoltra az elhelyezéséhez.\n\nA megerősítéshez nyomd meg a jobb alsó sarokban lévő \ue800 [accent]pipát[]. +gz.research = Nyisd meg a \ue875 Technológia fát.\nFejleszd ki a \uf870 [accent]Mechanikus fúrót[], majd válaszd ki a jobb alsó sarokban lévő menüből.\nKattints egy rézfoltra az elhelyezéséhez. +gz.research.mobile = Nyisd meg a \ue875 Technológia fát.\nFejleszd ki a \uf870 [accent]Mechanikus fúrót[], majd válaszd ki a jobb alsó sarokban lévő menüből.\nKattints egy rézfoltra az elhelyezéséhez.\n\nA megerősítéshez nyomd meg a jobb alsó sarokban lévő \ue800 [accent]pipát[]. gz.conveyors = Fejleszd ki, és építs \uf896 [accent]Szállítószalagokat[], hogy a kitermelt\nnyersanyagokat eljuttasd a fúróktól a támaszpontba.\n\nKattints és húzd az egeret, hogy több szállítószalagot helyezz el.\nHasználd a [accent]görgőt[] a forgatáshoz. gz.conveyors.mobile = Fejleszd ki, és építs \uf896 [accent]Szállítószalagokat[], hogy a kitermelt\nnyersanyagokat eljuttasd a fúróktól a támaszpontba.\n\nTartsd lenyomva az ujjad és húzd el, hogy több szállítószalagot helyezz el. gz.drills = Bővítsd a bányászati kapacitást.\nÉpíts több mechanikus fúrót.\nBányássz 100 rezet. @@ -1954,7 +1954,7 @@ gz.finish = Építs több lövegtornyot, bányássz több nyersanyagot,\nés vé onset.mine = Kattints bal egérgombbal a \uf748 [accent]berillium[] kibányászáshoz a falakból.\n\nA mozgáshoz használd a [accent][[WASD] gombokat. onset.mine.mobile = Koppints a \uf748 [accent]berillium[] kibányászáshoz a falakból. -onset.research = Nyisd meg a \ue875 fejlesztési fát.\nFejleszd ki, és építs egy \uf73e [accent]Kondenzációs turbinát[] a kürtőn.\nEz [accent]áramot[] fog termelni. +onset.research = Nyisd meg a \ue875 Technológia fát.\nFejleszd ki, és építs egy \uf73e [accent]Kondenzációs turbinát[] a kürtőn.\nEz [accent]áramot[] fog termelni. onset.bore = Fejleszd ki, és építs egy \uf741 [accent]Plazmafúrót[].\nEz automatikusan bányássza ki a nyersanyagokat a falakból. onset.power = Ahhoz, hogy [accent]árammal[] lásd el a plazmafúrót, fejleszd ki, és helyezz el egy \uf73d [accent]Sugárcsomópontot[].\nSegítségükkel összekötheted a kondenzációs turbinát a plazmafúróval. onset.ducts = Fejleszd ki, és építs \uf799 [accent]Szállítószalagot[], hogy a kitermelt nyersanyagokat eljuttasd a plazmafúrótól a támaszpontba.\nKattints, és húzd az egeret több szállítószalag elhelyezéséhez.\nHasználd a [accent]görgőt[] a forgatáshoz. From 47a475f1787013248373cd292308e37129a8d240 Mon Sep 17 00:00:00 2001 From: Ettaby <89039802+Ettaby@users.noreply.github.com> Date: Mon, 20 May 2024 16:33:49 +0300 Subject: [PATCH 203/348] 2D2R (Update servers_v7.json) (#9866) Added 2D2R server. - [x] I have read the contribution guidelines. - [x] I have ensured that my code compiles, if applicable. - [x] I have ensured that any new features in this PR function correctly in-game, if applicable. --- servers_v7.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/servers_v7.json b/servers_v7.json index 6a03d7a0ec..d54ec6c5af 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -295,5 +295,9 @@ { "name": "Impact", "address": ["impactmindustry.ddns.net"] + }, + { + "name": "2D2R", + "address": ["51.89.173.50:1284", "45.140.142.231:1045", "51.89.173.50:1432", "45.140.142.231:1052", "45.140.142.231:1040"] } ] From 779a8f75bceefb666da5fdaeda09e3b7ccdbed8d Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 23 May 2024 11:54:05 -0400 Subject: [PATCH 204/348] Fixed #9876 --- core/src/mindustry/world/blocks/distribution/Conveyor.java | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/world/blocks/distribution/Conveyor.java b/core/src/mindustry/world/blocks/distribution/Conveyor.java index ede7e1f269..1d9b895049 100644 --- a/core/src/mindustry/world/blocks/distribution/Conveyor.java +++ b/core/src/mindustry/world/blocks/distribution/Conveyor.java @@ -253,7 +253,7 @@ public class Conveyor extends Block implements Autotiler{ mid = 0; //skip updates if possible - if(len == 0){ + if(len == 0 && Mathf.equal(timeScale, 1f)){ clogHeat = 0f; sleep(); return; diff --git a/gradle.properties b/gradle.properties index a8cfe6a7bc..8226af606b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=7e559036ec +archash=5a944efcc0 From b4b6b9fa441b4c693082f49f5feec7f14fe1d729 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 24 May 2024 21:46:15 -0400 Subject: [PATCH 205/348] World processor list in editor --- .../annotations/impl/AssetsProcess.java | 5 + core/assets/bundles/bundle.properties | 7 + core/src/mindustry/Vars.java | 7 + core/src/mindustry/ai/types/LogicAI.java | 10 -- .../mindustry/ctype/UnlockableContent.java | 5 + .../src/mindustry/editor/MapEditorDialog.java | 7 +- core/src/mindustry/editor/MapInfoDialog.java | 20 ++- .../mindustry/editor/MapProcessorsDialog.java | 155 ++++++++++++++++++ core/src/mindustry/logic/LCanvas.java | 6 + core/src/mindustry/logic/LogicDialog.java | 23 ++- core/src/mindustry/ui/Fonts.java | 8 +- core/src/mindustry/ui/Styles.java | 10 ++ .../mindustry/ui/dialogs/DatabaseDialog.java | 4 +- .../ui/dialogs/IconSelectDialog.java | 74 +++++++++ .../mindustry/ui/dialogs/PausedDialog.java | 15 +- .../mindustry/ui/dialogs/PlanetDialog.java | 10 +- .../ui/dialogs/SchematicsDialog.java | 3 +- .../world/blocks/logic/LogicBlock.java | 69 +++++++- gradle.properties | 2 +- 19 files changed, 395 insertions(+), 45 deletions(-) create mode 100644 core/src/mindustry/editor/MapProcessorsDialog.java create mode 100644 core/src/mindustry/ui/dialogs/IconSelectDialog.java diff --git a/annotations/src/main/java/mindustry/annotations/impl/AssetsProcess.java b/annotations/src/main/java/mindustry/annotations/impl/AssetsProcess.java index 5ca28c0370..64b12ca33d 100644 --- a/annotations/src/main/java/mindustry/annotations/impl/AssetsProcess.java +++ b/annotations/src/main/java/mindustry/annotations/impl/AssetsProcess.java @@ -57,6 +57,9 @@ public class AssetsProcess extends BaseProcessor{ ichtype.addField(FieldSpec.builder(ParameterizedTypeName.get(ObjectIntMap.class, String.class), "codes", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("new ObjectIntMap<>()").build()); + ichtype.addField(FieldSpec.builder(ParameterizedTypeName.get(IntMap.class, String.class), + "codeToName", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("new IntMap<>()").build()); + ObjectSet used = new ObjectSet<>(); for(Jval val : icons.get("glyphs").asArray()){ @@ -67,7 +70,9 @@ public class AssetsProcess extends BaseProcessor{ int code = val.getInt("code", 0); iconcAll.append((char)code); ichtype.addField(FieldSpec.builder(char.class, name, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).addJavadoc(String.format("\\u%04x", code)).initializer("'" + ((char)code) + "'").build()); + ichinit.addStatement("codes.put($S, $L)", name, code); + ichinit.addStatement("codeToName.put($L, $S)", code, name); ictype.addField(TextureRegionDrawable.class, name + "Small", Modifier.PUBLIC, Modifier.STATIC); icload.addStatement(name + "Small = mindustry.ui.Fonts.getGlyph(mindustry.ui.Fonts.def, (char)" + code + ")"); diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 642330901d..eea3c1046d 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -445,6 +445,11 @@ editor.rules = Rules editor.generation = Generation editor.objectives = Objectives editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Edit In-Game editor.playtest = Playtest editor.publish.workshop = Publish On Workshop @@ -501,7 +506,9 @@ editor.default = [lightgray] details = Details... edit = Edit variables = Vars +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables + editor.name = Name: editor.spawn = Spawn Unit editor.removeunit = Remove Unit diff --git a/core/src/mindustry/Vars.java b/core/src/mindustry/Vars.java index 552f0ac54f..3538a39ce6 100644 --- a/core/src/mindustry/Vars.java +++ b/core/src/mindustry/Vars.java @@ -137,6 +137,13 @@ public class Vars implements Loadable{ Color.valueOf("4b5ef1"), Color.valueOf("2cabfe"), }; + /** Icons available to the user for customization in certain dialogs. */ + public static final String[] accessibleIcons = { + "effect", "power", "logic", "units", "liquid", "production", "defense", "turret", "distribution", "crafting", + "settings", "cancel", "zoom", "ok", "star", "home", "pencil", "up", "down", "left", "right", + "hammer", "warning", "tree", "admin", "map", "modePvp", "terrain", + "modeSurvival", "commandRally", "commandAttack", + }; /** maximum TCP packet size */ public static final int maxTcpSize = 900; /** default server port */ diff --git a/core/src/mindustry/ai/types/LogicAI.java b/core/src/mindustry/ai/types/LogicAI.java index 9486ebea42..f6be1965b3 100644 --- a/core/src/mindustry/ai/types/LogicAI.java +++ b/core/src/mindustry/ai/types/LogicAI.java @@ -40,8 +40,6 @@ public class LogicAI extends AIController{ public PosTeam posTarget = PosTeam.create(); private ObjectSet radars = new ObjectSet<>(); - private float lastMoveX, lastMoveY; - private int lastPathId = 0; // LogicAI state should not be reset after reading. @Override @@ -51,14 +49,6 @@ public class LogicAI extends AIController{ @Override public void updateMovement(){ - if(control == LUnitControl.pathfind){ - if(!Mathf.equal(moveX, lastMoveX, 0.1f) || !Mathf.equal(moveY, lastMoveY, 0.1f)){ - lastPathId ++; - lastMoveX = moveX; - lastMoveY = moveY; - } - } - if(targetTimer > 0f){ targetTimer -= Time.delta; }else{ diff --git a/core/src/mindustry/ctype/UnlockableContent.java b/core/src/mindustry/ctype/UnlockableContent.java index 434be84c4f..2692c28a94 100644 --- a/core/src/mindustry/ctype/UnlockableContent.java +++ b/core/src/mindustry/ctype/UnlockableContent.java @@ -147,6 +147,11 @@ public abstract class UnlockableContent extends MappableContent{ return Fonts.getUnicodeStr(name); } + public int emojiChar(){ + return Fonts.getUnicode(name); + } + + public boolean hasEmoji(){ return Fonts.hasUnicodeStr(name); } diff --git a/core/src/mindustry/editor/MapEditorDialog.java b/core/src/mindustry/editor/MapEditorDialog.java index 56ce52d8e1..a4c2de9dc3 100644 --- a/core/src/mindustry/editor/MapEditorDialog.java +++ b/core/src/mindustry/editor/MapEditorDialog.java @@ -24,7 +24,6 @@ import mindustry.gen.*; import mindustry.graphics.*; import mindustry.io.*; import mindustry.maps.*; -import mindustry.type.*; import mindustry.ui.*; import mindustry.ui.dialogs.*; import mindustry.world.*; @@ -212,11 +211,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ margin(0); update(() -> { - if(Core.scene.getKeyboardFocus() instanceof Dialog && Core.scene.getKeyboardFocus() != this){ - return; - } - - if(Core.scene != null && Core.scene.getKeyboardFocus() == this){ + if(hasKeyboard()){ doInput(); } }); diff --git a/core/src/mindustry/editor/MapInfoDialog.java b/core/src/mindustry/editor/MapInfoDialog.java index 86a217e82e..ebc1f0b300 100644 --- a/core/src/mindustry/editor/MapInfoDialog.java +++ b/core/src/mindustry/editor/MapInfoDialog.java @@ -14,16 +14,15 @@ import mindustry.ui.dialogs.*; import static mindustry.Vars.*; public class MapInfoDialog extends BaseDialog{ - private final WaveInfoDialog waveInfo; - private final MapGenerateDialog generate; - private final CustomRulesDialog ruleInfo = new CustomRulesDialog(); - private final MapObjectivesDialog objectives = new MapObjectivesDialog(); - private final MapLocalesDialog locales = new MapLocalesDialog(); + private WaveInfoDialog waveInfo = new WaveInfoDialog(); + private MapGenerateDialog generate = new MapGenerateDialog(false); + private CustomRulesDialog ruleInfo = new CustomRulesDialog(); + private MapObjectivesDialog objectives = new MapObjectivesDialog(); + private MapLocalesDialog locales = new MapLocalesDialog(); + private MapProcessorsDialog processors = new MapProcessorsDialog(); public MapInfoDialog(){ super("@editor.mapinfo"); - this.waveInfo = new WaveInfoDialog(); - this.generate = new MapGenerateDialog(false); addCloseButton(); @@ -108,7 +107,12 @@ public class MapInfoDialog extends BaseDialog{ ui.showException(e); } hide(); - }).marginLeft(10f).width(0f).colspan(2).center().growX(); + }).marginLeft(10f); + + r.button("@editor.worldprocessors", Icon.logic, style, () -> { + hide(); + processors.show(); + }).marginLeft(10f); }).colspan(2).center(); name.change(); diff --git a/core/src/mindustry/editor/MapProcessorsDialog.java b/core/src/mindustry/editor/MapProcessorsDialog.java new file mode 100644 index 0000000000..593ef429bb --- /dev/null +++ b/core/src/mindustry/editor/MapProcessorsDialog.java @@ -0,0 +1,155 @@ +package mindustry.editor; + +import arc.scene.style.*; +import arc.scene.ui.*; +import arc.scene.ui.layout.*; +import arc.struct.*; +import arc.util.*; +import mindustry.*; +import mindustry.content.*; +import mindustry.game.*; +import mindustry.gen.*; +import mindustry.ui.*; +import mindustry.ui.dialogs.*; +import mindustry.world.*; +import mindustry.world.blocks.environment.*; +import mindustry.world.blocks.logic.*; +import mindustry.world.blocks.logic.LogicBlock.*; + +import static mindustry.Vars.*; + +public class MapProcessorsDialog extends BaseDialog{ + private IconSelectDialog iconSelect = new IconSelectDialog(); + private TextField search; + private Seq processors = new Seq<>(); + private Table list; + + public MapProcessorsDialog(){ + super("@editor.worldprocessors"); + + shown(this::setup); + + addCloseButton(); + buttons.button("@add", Icon.add, () -> { + boolean foundAny = false; + + outer: + for(int y = 0; y < Vars.world.height(); y++){ + for(int x = 0; x < Vars.world.width(); x++){ + Tile tile = Vars.world.rawTile(x, y); + if(!tile.synthetic()){ + foundAny = true; + tile.setNet(Blocks.worldProcessor, Team.sharded, 0); + if(ui.editor.isShown()){ + Vars.editor.renderer.updatePoint(x, y); + } + break outer; + } + } + } + + if(!foundAny){ + ui.showErrorMessage("@editor.worldprocessors.nospace"); + }else{ + setup(); + } + }).size(210f, 64f); + + cont.top(); + getCell(cont).grow(); + + cont.table(s -> { + s.image(Icon.zoom).padRight(8); + search = s.field(null, text -> rebuild()).growX().get(); + search.setMessageText("@players.search"); + }).width(440f).fillX().padBottom(4).row(); + + cont.pane(t -> { + list = t; + }); + } + + private void rebuild(){ + list.clearChildren(); + + if(processors.isEmpty()){ + list.add("@editor.worldprocessors.none"); + }else{ + Table t = list; + var text = search.getText().toLowerCase(); + + t.defaults().pad(4f); + float h = 50f; + for(var build : processors){ + if(build instanceof LogicBuild log && (text.isEmpty() || (log.tag != null && log.tag.toLowerCase().contains(text)))){ + + t.button(log.iconTag == 0 ? Styles.none : new TextureRegionDrawable(Fonts.getLargeIcon(Fonts.unicodeToName(log.iconTag))), Styles.graySquarei, iconMed, () -> { + iconSelect.show(ic -> { + log.iconTag = (char)ic; + rebuild(); + }); + }).size(h); + + t.button((log.tag == null ? "\n" : "[accent]" + log.tag + "\n") + "[lightgray][[" + log.tile.x + ", " + log.tile.y + "]", Styles.grayt, () -> { + //TODO: bug: if you edit name inside of the edit dialog, it won't show up in the list properly + log.showEditDialog(); + }).size(Vars.mobile ? 390f : 450f, h).margin(10f).with(b -> { + b.getLabel().setAlignment(Align.left, Align.left); + }); + + t.button(Icon.pencil, Styles.graySquarei, Vars.iconMed, () -> { + ui.showTextInput("", "@editor.name", LogicBlock.maxNameLength, log.tag == null ? "" : log.tag, tag -> { + //bypass configuration and set it directly in case privileged checks mess things up + log.tag = tag; + setup(); + }); + }).size(h); + + if(Vars.state.isGame() && state.isEditor()){ + t.button(Icon.eyeSmall, Styles.graySquarei, Vars.iconMed, () -> { + hide(); + control.input.config.showConfig(build); + control.input.panCamera(Tmp.v1.set(build)); + }).size(h); + } + + t.button(Icon.trash, Styles.graySquarei, iconMed, () -> { + ui.showConfirm("@editor.worldprocessors.delete.confirm", () -> { + boolean surrounded = true; + for(int i = 0; i < 4; i++){ + Tile other = build.tile.nearby(i); + if(other != null && !(other.block().privileged || other.block().isStatic())){ + surrounded = false; + break; + } + } + if(surrounded){ + build.tile.setNet(build.tile.floor().wall instanceof StaticWall ? build.tile.floor().wall : Blocks.stoneWall); + }else{ + build.tile.setNet(Blocks.air); + } + processors.remove(build); + rebuild(); + }); + }).size(h); + + t.row(); + } + } + } + } + + private void setup(){ + + processors.clear(); + + //scan the entire world for processor (Groups.build can be empty, indexer is probably inaccurate) + Vars.world.tiles.eachTile(t -> { + if(t.isCenter() && t.block() == Blocks.worldProcessor){ + processors.add(t.build); + } + }); + + rebuild(); + } +} diff --git a/core/src/mindustry/logic/LCanvas.java b/core/src/mindustry/logic/LCanvas.java index d8c28c0464..04871c02fb 100644 --- a/core/src/mindustry/logic/LCanvas.java +++ b/core/src/mindustry/logic/LCanvas.java @@ -164,6 +164,12 @@ public class LCanvas extends Table{ this.statements.layout(); } + public void clearStatements(){ + jumps.clear(); + statements.clearChildren(); + statements.layout(); + } + StatementElem checkHovered(){ Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true); if(e != null){ diff --git a/core/src/mindustry/logic/LogicDialog.java b/core/src/mindustry/logic/LogicDialog.java index e49928e572..d35d040870 100644 --- a/core/src/mindustry/logic/LogicDialog.java +++ b/core/src/mindustry/logic/LogicDialog.java @@ -17,6 +17,7 @@ import mindustry.logic.LExecutor.*; import mindustry.logic.LStatements.*; import mindustry.ui.*; import mindustry.ui.dialogs.*; +import mindustry.world.blocks.logic.*; import static mindustry.Vars.*; import static mindustry.logic.LCanvas.*; @@ -92,11 +93,29 @@ public class LogicDialog extends BaseDialog{ TextButtonStyle style = Styles.flatt; t.defaults().size(280f, 60f).left(); + if(privileged && executor != null && executor.build != null){// && !ui.editor.isShown() + t.button("@editor.worldprocessors.editname", Icon.edit, style, () -> { + ui.showTextInput("", "@editor.name", LogicBlock.maxNameLength, executor.build.tag == null ? "" : executor.build.tag, tag -> { + if(privileged && executor != null && executor.build != null){ + executor.build.configure(tag); + //just in case of privilege shenanigans... + executor.build.tag = tag; + } + }); + dialog.hide(); + }).marginLeft(12f).row(); + } + + t.button("@clear", Icon.cancel, style, () -> { + ui.showConfirm("@logic.clear.confirm", () -> canvas.clearStatements()); + dialog.hide(); + }).marginLeft(12f).row(); + t.button("@schematic.copy", Icon.copy, style, () -> { dialog.hide(); Core.app.setClipboardText(canvas.save()); - }).marginLeft(12f); - t.row(); + }).marginLeft(12f).row(); + t.button("@schematic.copy.import", Icon.download, style, () -> { dialog.hide(); try{ diff --git a/core/src/mindustry/ui/Fonts.java b/core/src/mindustry/ui/Fonts.java index 6dd4ff1c24..29945aa9d2 100644 --- a/core/src/mindustry/ui/Fonts.java +++ b/core/src/mindustry/ui/Fonts.java @@ -30,6 +30,7 @@ public class Fonts{ private static final String mainFont = "fonts/font.woff"; private static final ObjectSet unscaled = ObjectSet.with("iconLarge"); private static ObjectIntMap unicodeIcons = new ObjectIntMap<>(); + private static IntMap unicodeToName = new IntMap<>(); private static ObjectMap stringIcons = new ObjectMap<>(); private static ObjectMap largeIcons = new ObjectMap<>(); private static TextureRegion[] iconTable; @@ -95,12 +96,16 @@ public class Fonts{ }})).loaded = f -> Fonts.logic = f; } + public static @Nullable String unicodeToName(int unicode){ + return unicodeToName.get(unicode, () -> Iconc.codeToName.get(unicode)); + } + public static TextureRegion getLargeIcon(String name){ return largeIcons.get(name, () -> { var region = new TextureRegion(); int code = Iconc.codes.get(name, '\uF8D4'); var glyph = iconLarge.getData().getGlyph((char)code); - if(glyph == null) return Core.atlas.find("error"); + if(glyph == null) return Core.atlas.find(name); region.set(iconLarge.getRegion().texture); region.set(glyph.u, glyph.v2, glyph.u2, glyph.v); return region; @@ -127,6 +132,7 @@ public class Fonts{ unicodeIcons.put(nametex[0], ch); stringIcons.put(nametex[0], ((char)ch) + ""); + unicodeToName.put(ch, texture); Vec2 out = Scaling.fit.apply(region.width, region.height, size, size); diff --git a/core/src/mindustry/ui/Styles.java b/core/src/mindustry/ui/Styles.java index 416aeb3c2b..b25c2d70c2 100644 --- a/core/src/mindustry/ui/Styles.java +++ b/core/src/mindustry/ui/Styles.java @@ -73,6 +73,8 @@ public class Styles{ geni, /** Gray, toggleable, no background. */ grayi, + /** Gray square background, standard behavior. Equivalent to grayt. */ + graySquarei, /** Flat, square, black background. */ flati, /** Square border. */ @@ -288,6 +290,14 @@ public class Styles{ imageUpColor = Color.lightGray; imageDownColor = Color.white; }}; + graySquarei = new ImageButtonStyle(){{ + imageUpColor = Color.white; + imageDownColor = Color.lightGray; + + over = flatOver; + down = flatOver; + up = grayPanel; + }}; flati = new ImageButtonStyle(){{ down = flatOver; up = black; diff --git a/core/src/mindustry/ui/dialogs/DatabaseDialog.java b/core/src/mindustry/ui/dialogs/DatabaseDialog.java index 9ef878c280..9fdefa9e1c 100644 --- a/core/src/mindustry/ui/dialogs/DatabaseDialog.java +++ b/core/src/mindustry/ui/dialogs/DatabaseDialog.java @@ -45,7 +45,7 @@ public class DatabaseDialog extends BaseDialog{ void rebuild(){ all.clear(); - var text = search.getText(); + var text = search.getText().toLowerCase(); Seq[] allContent = Vars.content.getContentMap(); @@ -54,7 +54,7 @@ public class DatabaseDialog extends BaseDialog{ Seq array = allContent[j] .select(c -> c instanceof UnlockableContent u && !u.isHidden() && - (text.isEmpty() || u.localizedName.toLowerCase().contains(text.toLowerCase()))).as(); + (text.isEmpty() || u.localizedName.toLowerCase().contains(text))).as(); if(array.size == 0) continue; all.add("@content." + type.name() + ".name").growX().left().color(Pal.accent); diff --git a/core/src/mindustry/ui/dialogs/IconSelectDialog.java b/core/src/mindustry/ui/dialogs/IconSelectDialog.java new file mode 100644 index 0000000000..153d25761d --- /dev/null +++ b/core/src/mindustry/ui/dialogs/IconSelectDialog.java @@ -0,0 +1,74 @@ +package mindustry.ui.dialogs; + +import arc.*; +import arc.func.*; +import arc.scene.style.*; +import arc.scene.ui.*; +import arc.scene.ui.layout.*; +import arc.util.*; +import mindustry.ctype.*; +import mindustry.gen.*; +import mindustry.graphics.*; +import mindustry.ui.*; + +import static mindustry.Vars.*; + +public class IconSelectDialog extends Dialog{ + private Intc consumer = i -> Log.info("you have mere seconds"); + + public IconSelectDialog(){ + closeOnBack(); + setFillParent(true); + + cont.pane(t -> { + resized(true, () -> { + t.clearChildren(); + t.marginRight(19f); + t.defaults().size(48f); + + t.button(Icon.none, Styles.flati, () -> { + hide(); + consumer.get(0); + }); + + int cols = (int)Math.min(20, Core.graphics.getWidth() / Scl.scl(52f)); + + int i = 1; + for(var key : accessibleIcons){ + var value = Icon.icons.get(key); + + t.button(value, Styles.flati, () -> { + hide(); + consumer.get(Iconc.codes.get(key)); + }); + + if(++i % cols == 0) t.row(); + } + + for(ContentType ctype : defaultContentIcons){ + t.row(); + t.image().colspan(cols).growX().width(Float.NEGATIVE_INFINITY).height(3f).color(Pal.accent); + t.row(); + + i = 0; + for(UnlockableContent u : content.getBy(ctype).as()){ + if(!u.isHidden() && u.unlocked()){ + t.button(new TextureRegionDrawable(u.uiIcon), Styles.flati, iconMed, () -> { + hide(); + consumer.get(u.emojiChar()); + }); + + if(++i % cols == 0) t.row(); + } + } + } + }); + }); + buttons.button("@back", Icon.left, this::hide).size(210f, 64f); + } + + public void show(Intc listener){ + consumer = listener; + super.show(); + } +} diff --git a/core/src/mindustry/ui/dialogs/PausedDialog.java b/core/src/mindustry/ui/dialogs/PausedDialog.java index 129e64d658..b854aa1eb9 100644 --- a/core/src/mindustry/ui/dialogs/PausedDialog.java +++ b/core/src/mindustry/ui/dialogs/PausedDialog.java @@ -1,11 +1,13 @@ package mindustry.ui.dialogs; import arc.*; +import mindustry.editor.*; import mindustry.gen.*; import static mindustry.Vars.*; public class PausedDialog extends BaseDialog{ + private MapProcessorsDialog processors = new MapProcessorsDialog(); private SaveDialog save = new SaveDialog(); private LoadDialog load = new LoadDialog(); private boolean wasClient = false; @@ -49,13 +51,22 @@ public class PausedDialog extends BaseDialog{ cont.row(); - cont.button("@hostserver", Icon.host, () -> { + //the button runs out of space when the editor button is added, so use the mobile text + cont.button(state.isEditor() ? "@hostserver.mobile" : "@hostserver", Icon.host, () -> { if(net.server() && steam){ platform.inviteFriends(); }else{ ui.host.show(); } - }).disabled(b -> !((steam && net.server()) || !net.active())).colspan(2).width(dw * 2 + 10f).update(e -> e.setText(net.server() && steam ? "@invitefriends" : "@hostserver")); + }).disabled(b -> !((steam && net.server()) || !net.active())).colspan(state.isEditor() ? 1 : 2).width(state.isEditor() ? dw : dw * 2 + 10f) + .update(e -> e.setText(net.server() && steam ? "@invitefriends" : state.isEditor() ? "@hostserver.mobile" : "@hostserver")); + + if(state.isEditor()){ + cont.button("@editor.worldprocessors", Icon.logic, () -> { + hide(); + processors.show(); + }); + } cont.row(); diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index 0c43dbff42..c8f4fb44c2 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -43,13 +43,6 @@ import static mindustry.graphics.g3d.PlanetRenderer.*; import static mindustry.ui.dialogs.PlanetDialog.Mode.*; public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ - static final String[] defaultIcons = { - "effect", "power", "logic", "units", "liquid", "production", "defense", "turret", "distribution", "crafting", - "settings", "cancel", "zoom", "ok", "star", "home", "pencil", "up", "down", "left", "right", - "hammer", "warning", "tree", "admin", "map", "modePvp", "terrain", - "modeSurvival", "commandRally", "commandAttack", - }; - //if true, enables launching anywhere for testing public static boolean debugSelect = false; public static float sectorShowDuration = 60f * 2.4f; @@ -1054,6 +1047,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ Icon.icons.get(sector.info.icon + "Small"); title.button(icon == null ? Icon.noneSmall : icon, Styles.clearNonei, iconSmall, () -> { + //TODO use IconSelectDialog new Dialog(""){{ closeOnBack(); setFillParent(true); @@ -1080,7 +1074,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ int cols = (int)Math.min(20, Core.graphics.getWidth() / Scl.scl(52f)); int i = 1; - for(var key : defaultIcons){ + for(var key : accessibleIcons){ var value = Icon.icons.get(key); t.button(value, Styles.squareTogglei, () -> { diff --git a/core/src/mindustry/ui/dialogs/SchematicsDialog.java b/core/src/mindustry/ui/dialogs/SchematicsDialog.java index ff444b8991..3907f10dbe 100644 --- a/core/src/mindustry/ui/dialogs/SchematicsDialog.java +++ b/core/src/mindustry/ui/dialogs/SchematicsDialog.java @@ -398,6 +398,7 @@ public class SchematicsDialog extends BaseDialog{ closeOnBack(); setFillParent(true); + //TODO: use IconSelectDialog cont.pane(t -> { resized(true, () -> { t.clearChildren(); @@ -407,7 +408,7 @@ public class SchematicsDialog extends BaseDialog{ int cols = (int)Math.min(20, Core.graphics.getWidth() / Scl.scl(52f)); int i = 0; - for(String icon : PlanetDialog.defaultIcons){ + for(String icon : accessibleIcons){ String out = (char)Iconc.codes.get(icon) + ""; if(tags.contains(out)) continue; diff --git a/core/src/mindustry/world/blocks/logic/LogicBlock.java b/core/src/mindustry/world/blocks/logic/LogicBlock.java index 3b5abe456e..764126991b 100644 --- a/core/src/mindustry/world/blocks/logic/LogicBlock.java +++ b/core/src/mindustry/world/blocks/logic/LogicBlock.java @@ -3,6 +3,8 @@ package mindustry.world.blocks.logic; import arc.Graphics.*; import arc.Graphics.Cursor.*; import arc.func.*; +import arc.graphics.*; +import arc.graphics.g2d.*; import arc.math.*; import arc.math.geom.*; import arc.scene.ui.layout.*; @@ -10,6 +12,7 @@ import arc.struct.Bits; import arc.struct.*; import arc.util.*; import arc.util.io.*; +import arc.util.pooling.*; import mindustry.ai.types.*; import mindustry.core.*; import mindustry.gen.*; @@ -31,6 +34,7 @@ import static mindustry.Vars.*; public class LogicBlock extends Block{ private static final int maxByteLen = 1024 * 100; + public static final int maxNameLength = 32; public int maxInstructionScale = 5; public int instructionsPerTick = 1; @@ -56,6 +60,14 @@ public class LogicBlock extends Block{ build.readCompressed(data, true); }); + config(String.class,(LogicBuild build, String data) -> { + if(!accessible() || !privileged) return; + + if(data != null && data.length() < maxNameLength){ + build.tag = data; + } + }); + config(Integer.class, (LogicBuild entity, Integer pos) -> { if(!accessible()) return; @@ -235,6 +247,9 @@ public class LogicBlock extends Block{ public boolean checkedDuplicates = false; //dynamic only for privileged processors public int ipt = instructionsPerTick; + /** Display name, for convenience. This is currently only available for world processors. */ + public @Nullable String tag; + public char iconTag; /** Block of code to run after load. */ public @Nullable Runnable loadBlock; @@ -563,9 +578,45 @@ public class LogicBlock extends Block{ @Override public void drawSelect(){ + if(!accessible()) return; + Groups.unit.each(u -> u.controller() instanceof LogicAI ai && ai.controller == this, unit -> { Drawf.square(unit.x, unit.y, unit.hitSize, unit.rotation + 45); }); + + //draw tag over processor (world processor only) + if(!(renderer.pixelate || !privileged || tag == null || tag.isEmpty())){ + Font font = Fonts.outline; + GlyphLayout l = Pools.obtain(GlyphLayout.class, GlyphLayout::new); + boolean ints = font.usesIntegerPositions(); + font.getData().setScale(1 / 4f / Scl.scl(1f)); + font.setUseIntegerPositions(false); + + l.setText(font, tag, Color.white, 90f, Align.left, true); + float offset = 1f; + + //Draw.color(0f, 0f, 0f, 0.1f); + //Fill.rect(x, y + tilesize/2f - l.height/2f - offset, l.width + offset*2f, l.height + offset*2f); + Draw.color(); + font.setColor(1f, 1f, 1f, 0.5f); + font.draw(tag, x - l.width/2f, y + tilesize + 2f - offset, 90f, Align.left, true); + font.setUseIntegerPositions(ints); + + font.getData().setScale(1f); + + Pools.free(l); + } + + if(iconTag != 0){ + TextureRegion icon = Fonts.getLargeIcon(Fonts.unicodeToName(iconTag)); + if(icon.found()){ + Draw.alpha(0.5f); + + Draw.rect(icon, x, y, tilesize, tilesize / icon.ratio()); + + Draw.color(); + } + } } public boolean validLink(Building other){ @@ -579,9 +630,11 @@ public class LogicBlock extends Block{ @Override public void buildConfiguration(Table table){ - table.button(Icon.pencil, Styles.cleari, () -> { - ui.logic.show(code, executor, privileged, code -> configure(compress(code, relativeConnections()))); - }).size(40); + table.button(Icon.pencil, Styles.cleari, this::showEditDialog).size(40); + } + + public void showEditDialog(){ + ui.logic.show(code, executor, privileged, code -> configure(compress(code, relativeConnections()))); } @Override @@ -601,7 +654,7 @@ public class LogicBlock extends Block{ @Override public byte version(){ - return 2; + return 3; } @Override @@ -638,6 +691,9 @@ public class LogicBlock extends Block{ if(privileged){ write.s(Mathf.clamp(ipt, 1, maxInstructionsPerTick)); } + + TypeIO.writeString(write, tag); + write.s(iconTag); } @Override @@ -694,6 +750,11 @@ public class LogicBlock extends Block{ ipt = Mathf.clamp(read.s(), 1, maxInstructionsPerTick); } + if(revision >= 3){ + tag = TypeIO.readString(read); + iconTag = (char)read.us(); + } + } } } diff --git a/gradle.properties b/gradle.properties index 8226af606b..7dfd79eecb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=5a944efcc0 +archash=f0d4fdbf89 From 37dc449471b70317a4acaad97e068caa9fa43390 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 24 May 2024 22:02:37 -0400 Subject: [PATCH 206/348] Processor list bugfixes --- .../mindustry/editor/MapProcessorsDialog.java | 2 +- core/src/mindustry/logic/LogicDialog.java | 2 +- .../world/blocks/logic/LogicBlock.java | 20 +++++++++++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/core/src/mindustry/editor/MapProcessorsDialog.java b/core/src/mindustry/editor/MapProcessorsDialog.java index 593ef429bb..dd0f73d3f7 100644 --- a/core/src/mindustry/editor/MapProcessorsDialog.java +++ b/core/src/mindustry/editor/MapProcessorsDialog.java @@ -92,7 +92,7 @@ public class MapProcessorsDialog extends BaseDialog{ t.button((log.tag == null ? "\n" : "[accent]" + log.tag + "\n") + "[lightgray][[" + log.tile.x + ", " + log.tile.y + "]", Styles.grayt, () -> { //TODO: bug: if you edit name inside of the edit dialog, it won't show up in the list properly - log.showEditDialog(); + log.showEditDialog(true); }).size(Vars.mobile ? 390f : 450f, h).margin(10f).with(b -> { b.getLabel().setAlignment(Align.left, Align.left); }); diff --git a/core/src/mindustry/logic/LogicDialog.java b/core/src/mindustry/logic/LogicDialog.java index d35d040870..c6e227b424 100644 --- a/core/src/mindustry/logic/LogicDialog.java +++ b/core/src/mindustry/logic/LogicDialog.java @@ -93,7 +93,7 @@ public class LogicDialog extends BaseDialog{ TextButtonStyle style = Styles.flatt; t.defaults().size(280f, 60f).left(); - if(privileged && executor != null && executor.build != null){// && !ui.editor.isShown() + if(privileged && executor != null && executor.build != null && !ui.editor.isShown()){ t.button("@editor.worldprocessors.editname", Icon.edit, style, () -> { ui.showTextInput("", "@editor.name", LogicBlock.maxNameLength, executor.build.tag == null ? "" : executor.build.tag, tag -> { if(privileged && executor != null && executor.build != null){ diff --git a/core/src/mindustry/world/blocks/logic/LogicBlock.java b/core/src/mindustry/world/blocks/logic/LogicBlock.java index 764126991b..dc2b67aa31 100644 --- a/core/src/mindustry/world/blocks/logic/LogicBlock.java +++ b/core/src/mindustry/world/blocks/logic/LogicBlock.java @@ -60,7 +60,7 @@ public class LogicBlock extends Block{ build.readCompressed(data, true); }); - config(String.class,(LogicBuild build, String data) -> { + config(String.class, (LogicBuild build, String data) -> { if(!accessible() || !privileged) return; if(data != null && data.length() < maxNameLength){ @@ -68,6 +68,12 @@ public class LogicBlock extends Block{ } }); + config(Character.class, (LogicBuild build, Character data) -> { + if(!accessible() || !privileged) return; + + build.iconTag = data; + }); + config(Integer.class, (LogicBuild entity, Integer pos) -> { if(!accessible()) return; @@ -634,7 +640,17 @@ public class LogicBlock extends Block{ } public void showEditDialog(){ - ui.logic.show(code, executor, privileged, code -> configure(compress(code, relativeConnections()))); + showEditDialog(false); + } + + public void showEditDialog(boolean forceEditor){ + ui.logic.show(code, executor, privileged, code -> { + boolean prev = state.rules.editor; + //this is a hack to allow configuration to work correctly in the editor for privileged processors + if(forceEditor) state.rules.editor = true; + configure(compress(code, relativeConnections())); + state.rules.editor = prev; + }); } @Override From 4718771dcf09be60e2c51ffd265b098e14f24986 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 24 May 2024 22:15:48 -0400 Subject: [PATCH 207/348] WHAT DO YOU MEAN AUTHOR IDENTITY UNKNOWN --- .github/workflows/push.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 6d4dbbea84..b64e59fe52 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -34,6 +34,7 @@ jobs: if [ -n "$(git status --porcelain)" ]; then git config --global user.name "Github Actions" + git config --global user.email "cli@github.com" git add core/assets/bundles/* git commit -m "Automatic bundle update" git push From ecfb5ede51c9950c11cb7eb5724a53a701a31463 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Sat, 25 May 2024 02:16:35 +0000 Subject: [PATCH 208/348] Automatic bundle update --- core/assets/bundles/bundle_be.properties | 6 ++++++ core/assets/bundles/bundle_bg.properties | 6 ++++++ core/assets/bundles/bundle_ca.properties | 6 ++++++ core/assets/bundles/bundle_cs.properties | 6 ++++++ core/assets/bundles/bundle_da.properties | 6 ++++++ core/assets/bundles/bundle_de.properties | 6 ++++++ core/assets/bundles/bundle_es.properties | 6 ++++++ core/assets/bundles/bundle_et.properties | 6 ++++++ core/assets/bundles/bundle_eu.properties | 6 ++++++ core/assets/bundles/bundle_fi.properties | 6 ++++++ core/assets/bundles/bundle_fil.properties | 6 ++++++ core/assets/bundles/bundle_fr.properties | 6 ++++++ core/assets/bundles/bundle_hu.properties | 6 ++++++ core/assets/bundles/bundle_id_ID.properties | 6 ++++++ core/assets/bundles/bundle_it.properties | 6 ++++++ core/assets/bundles/bundle_ja.properties | 6 ++++++ core/assets/bundles/bundle_ko.properties | 6 ++++++ core/assets/bundles/bundle_lt.properties | 6 ++++++ core/assets/bundles/bundle_nl.properties | 6 ++++++ core/assets/bundles/bundle_nl_BE.properties | 6 ++++++ core/assets/bundles/bundle_pl.properties | 6 ++++++ core/assets/bundles/bundle_pt_BR.properties | 6 ++++++ core/assets/bundles/bundle_pt_PT.properties | 6 ++++++ core/assets/bundles/bundle_ro.properties | 6 ++++++ core/assets/bundles/bundle_ru.properties | 6 ++++++ core/assets/bundles/bundle_sr.properties | 6 ++++++ core/assets/bundles/bundle_sv.properties | 6 ++++++ core/assets/bundles/bundle_th.properties | 6 ++++++ core/assets/bundles/bundle_tk.properties | 6 ++++++ core/assets/bundles/bundle_tr.properties | 6 ++++++ core/assets/bundles/bundle_uk_UA.properties | 6 ++++++ core/assets/bundles/bundle_vi.properties | 6 ++++++ core/assets/bundles/bundle_zh_CN.properties | 6 ++++++ core/assets/bundles/bundle_zh_TW.properties | 6 ++++++ 34 files changed, 204 insertions(+) diff --git a/core/assets/bundles/bundle_be.properties b/core/assets/bundles/bundle_be.properties index 796e9809df..aef04b1cc4 100644 --- a/core/assets/bundles/bundle_be.properties +++ b/core/assets/bundles/bundle_be.properties @@ -434,6 +434,11 @@ editor.rules = Правілы: editor.generation = Генерацыя: editor.objectives = Мэты editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Рэдагаваць ў гульні editor.playtest = Тэставаць editor.publish.workshop = Апублікаваць у майстэрні @@ -489,6 +494,7 @@ editor.default = [lightgray]<Па змаўчанні> details = Падрабязнасці... edit = Рэдагаваць... variables = Пераменныя +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Назва: editor.spawn = Стварыць баявую адзінку diff --git a/core/assets/bundles/bundle_bg.properties b/core/assets/bundles/bundle_bg.properties index a78369d6c1..fdd40cde47 100644 --- a/core/assets/bundles/bundle_bg.properties +++ b/core/assets/bundles/bundle_bg.properties @@ -439,6 +439,11 @@ editor.rules = Правила: editor.generation = Генериране: editor.objectives = Objectives editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Редактирай в игра editor.playtest = Playtest editor.publish.workshop = Публикувай в Работилницата @@ -495,6 +500,7 @@ editor.default = [lightgray]<Стандартно> details = Детайли... edit = Редактирай... variables = Vars +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Име: editor.spawn = Създай Единица diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index aaf11f4a10..5165cdbe04 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -439,6 +439,11 @@ editor.rules = Regles editor.generation = Generació editor.objectives = Objectius editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Edita des de la partida editor.playtest = Prova el mapa editor.publish.workshop = Publica al Workshop @@ -495,6 +500,7 @@ editor.default = [lightgray] details = Detalls edit = Edita variables = Variables +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Nom: editor.spawn = Genera una unitat diff --git a/core/assets/bundles/bundle_cs.properties b/core/assets/bundles/bundle_cs.properties index 7ffd6977a9..2207a39196 100644 --- a/core/assets/bundles/bundle_cs.properties +++ b/core/assets/bundles/bundle_cs.properties @@ -440,6 +440,11 @@ editor.rules = Pravidla: editor.generation = Generace: editor.objectives = Úkoly: editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Upravit ve hře editor.playtest = Playtest editor.publish.workshop = Publikovat do Workshopu na Steamu @@ -496,6 +501,7 @@ editor.default = [lightgray][] details = Podrobnosti... edit = Upravit... variables = Hodnoty +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Jméno: editor.spawn = Zrodit jednotku diff --git a/core/assets/bundles/bundle_da.properties b/core/assets/bundles/bundle_da.properties index 6d0dccd596..ce7ac58a72 100644 --- a/core/assets/bundles/bundle_da.properties +++ b/core/assets/bundles/bundle_da.properties @@ -435,6 +435,11 @@ editor.rules = Regler: editor.generation = Generering: editor.objectives = Objectives editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Ændr i spil editor.playtest = Playtest editor.publish.workshop = Publicer på Workshop @@ -490,6 +495,7 @@ editor.default = [lightgray] details = Detaljer... edit = Rediger... variables = Vars +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Navn: editor.spawn = Påkald enhed diff --git a/core/assets/bundles/bundle_de.properties b/core/assets/bundles/bundle_de.properties index d12d5bb6c3..4ed332d921 100644 --- a/core/assets/bundles/bundle_de.properties +++ b/core/assets/bundles/bundle_de.properties @@ -442,6 +442,11 @@ editor.rules = Regeln editor.generation = Generator editor.objectives = Ziele editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Im Spiel bearbeiten editor.playtest = Playtest editor.publish.workshop = Im Workshop veröffentlichen @@ -498,6 +503,7 @@ editor.default = [lightgray] details = Details edit = Bearbeiten variables = Variablen +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Name: editor.spawn = Spawnbereich diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index 72f94349a8..f63857339a 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -439,6 +439,11 @@ editor.rules = Normas: editor.generation = Generación: editor.objectives = Objetivos editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Editar desde la nave editor.playtest = Probar mapa editor.publish.workshop = Publicar en Steam Workshop @@ -495,6 +500,7 @@ editor.default = [lightgray] details = Detalles... edit = Editar... variables = Variables +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Nombre: editor.spawn = Generar unidad diff --git a/core/assets/bundles/bundle_et.properties b/core/assets/bundles/bundle_et.properties index 2bd2079c43..855700b1d7 100644 --- a/core/assets/bundles/bundle_et.properties +++ b/core/assets/bundles/bundle_et.properties @@ -435,6 +435,11 @@ editor.rules = Reeglid: editor.generation = Genereerimine: editor.objectives = Objectives editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Redigeeri mängus editor.playtest = Playtest editor.publish.workshop = Avalda Workshop'is @@ -490,6 +495,7 @@ editor.default = [lightgray] details = Üksikasjad... edit = Muuda... variables = Vars +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Nimi: editor.spawn = Tekita väeüksus diff --git a/core/assets/bundles/bundle_eu.properties b/core/assets/bundles/bundle_eu.properties index 7cb2dcb491..48ef8c58fe 100644 --- a/core/assets/bundles/bundle_eu.properties +++ b/core/assets/bundles/bundle_eu.properties @@ -437,6 +437,11 @@ editor.rules = Arauak: editor.generation = Sorrarazi: editor.objectives = Objectives editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Editatu jolasean editor.playtest = Playtest editor.publish.workshop = Argitaratu lantegian @@ -492,6 +497,7 @@ editor.default = [lightgray] details = Xehetasunak... edit = Editatu... variables = Vars +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Izena: editor.spawn = Sortu unitatea diff --git a/core/assets/bundles/bundle_fi.properties b/core/assets/bundles/bundle_fi.properties index 5a308f3689..727fa81389 100644 --- a/core/assets/bundles/bundle_fi.properties +++ b/core/assets/bundles/bundle_fi.properties @@ -435,6 +435,11 @@ editor.rules = Säännöt: editor.generation = Generaatio: editor.objectives = Tehtävät editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Muokka pelin sisällä editor.playtest = Testaa pelin sisällä editor.publish.workshop = Julkaise Workshoppiin @@ -490,6 +495,7 @@ editor.default = [lightgray] details = Yksityiskohdat... edit = Muokkaa... variables = Muuttujat +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Nimi: editor.spawn = Luo yksikkö diff --git a/core/assets/bundles/bundle_fil.properties b/core/assets/bundles/bundle_fil.properties index b611eec885..4ab4e812af 100644 --- a/core/assets/bundles/bundle_fil.properties +++ b/core/assets/bundles/bundle_fil.properties @@ -435,6 +435,11 @@ editor.rules = Rules: editor.generation = Generation: editor.objectives = Objectives editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Edit In-Game editor.playtest = Playtest editor.publish.workshop = I-Publish Sa Workshop @@ -490,6 +495,7 @@ editor.default = [lightgray] details = Details... edit = Edit... variables = Vars +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Name: editor.spawn = Spawn Unit diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index 548d329ba1..64f90a2739 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -445,6 +445,11 @@ editor.rules = Règles editor.generation = Génération editor.objectives = Objectifs editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Éditer dans le jeu editor.playtest = Tester editor.publish.workshop = Publier sur le Workshop @@ -501,6 +506,7 @@ editor.default = [lightgray] details = Détails... edit = Modifier... variables = Variables +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Nom : editor.spawn = Ajouter une unité diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index e8bd332dbc..3b7f3f96d2 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -445,6 +445,11 @@ editor.rules = Szabályok editor.generation = Előállítás editor.objectives = Célok editor.locales = Helyi csomagok +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Szerkesztés a játékban editor.playtest = Teszt a játékban editor.publish.workshop = Közzététel a Steam Műhelyben @@ -501,6 +506,7 @@ editor.default = [lightgray] details = Részletek... edit = Szerkesztés variables = Változók +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Beépített változók editor.name = Név: editor.spawn = Egység létrehozása diff --git a/core/assets/bundles/bundle_id_ID.properties b/core/assets/bundles/bundle_id_ID.properties index bae545e043..c044097225 100644 --- a/core/assets/bundles/bundle_id_ID.properties +++ b/core/assets/bundles/bundle_id_ID.properties @@ -439,6 +439,11 @@ editor.rules = Peraturan: editor.generation = Generasi: editor.objectives = Tujuan editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Sunting dalam Permainan editor.playtest = Tes Bermain editor.publish.workshop = Terbitkan di Workshop @@ -495,6 +500,7 @@ editor.default = [lightgray] details = Detail... edit = Sunting... variables = Vars +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Nama: editor.spawn = Munculkan Unit diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties index 2507cd797a..c971e312b9 100644 --- a/core/assets/bundles/bundle_it.properties +++ b/core/assets/bundles/bundle_it.properties @@ -437,6 +437,11 @@ editor.rules = Regole: editor.generation = Generazione: editor.objectives = Obbiettivi editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Modifica in Gioco editor.playtest = Playtest editor.publish.workshop = Pubblica nel Workshop @@ -493,6 +498,7 @@ editor.default = [lightgray] details = Dettagli... edit = Modifica... variables = Vars +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Nome: editor.spawn = Piazza un'Unità diff --git a/core/assets/bundles/bundle_ja.properties b/core/assets/bundles/bundle_ja.properties index 47d38095ba..4448fcdf88 100644 --- a/core/assets/bundles/bundle_ja.properties +++ b/core/assets/bundles/bundle_ja.properties @@ -439,6 +439,11 @@ editor.rules = ルール: editor.generation = 生成: editor.objectives = オブジェクティブ editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = ゲーム内で編集する editor.playtest = Playtest editor.publish.workshop = ワークショップで公開 @@ -495,6 +500,7 @@ editor.default = [lightgray]<デフォルト> details = 詳細... edit = 編集... variables = 変数 +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = 名前: editor.spawn = ユニットを出す diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index 4bf96659d5..0210837253 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -438,6 +438,11 @@ editor.rules = 규칙 editor.generation = 지형 생성 editor.objectives = 목표 editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = 인게임 편집 editor.playtest = 맵 테스트 editor.publish.workshop = 창작마당 게시 @@ -494,6 +499,7 @@ editor.default = [lightgray]<기본값> details = 설명... edit = 편집... variables = 변수 +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = 이름: editor.spawn = 기체 생성 diff --git a/core/assets/bundles/bundle_lt.properties b/core/assets/bundles/bundle_lt.properties index 5ff000217e..5001761e3c 100644 --- a/core/assets/bundles/bundle_lt.properties +++ b/core/assets/bundles/bundle_lt.properties @@ -435,6 +435,11 @@ editor.rules = Taisyklės: editor.generation = Generacija: editor.objectives = Objectives editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Redaguoti žaidime editor.playtest = Playtest editor.publish.workshop = Publikuoti Dirbtuvėje @@ -490,6 +495,7 @@ editor.default = [lightgray] details = Detaliau... edit = Redaguoti... variables = Vars +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Pavadinimas: editor.spawn = Atradinti vienetą diff --git a/core/assets/bundles/bundle_nl.properties b/core/assets/bundles/bundle_nl.properties index 5e3a28968d..251ecf09b3 100644 --- a/core/assets/bundles/bundle_nl.properties +++ b/core/assets/bundles/bundle_nl.properties @@ -443,6 +443,11 @@ editor.rules = Regels: editor.generation = Generatie: editor.objectives = Doelen editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Bewerk In-Spel editor.playtest = Speeltest editor.publish.workshop = Publiceer in Werkplaats @@ -498,6 +503,7 @@ editor.default = [lightgray] details = Details... edit = Bewerk... variables = Vars +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Naam: editor.spawn = Voeg Eenheid toe diff --git a/core/assets/bundles/bundle_nl_BE.properties b/core/assets/bundles/bundle_nl_BE.properties index 930991bb72..dfcc1b9c0c 100644 --- a/core/assets/bundles/bundle_nl_BE.properties +++ b/core/assets/bundles/bundle_nl_BE.properties @@ -435,6 +435,11 @@ editor.rules = Rules: editor.generation = Generation: editor.objectives = Objectives editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Edit In-Game editor.playtest = Playtest editor.publish.workshop = Publish On Workshop @@ -490,6 +495,7 @@ editor.default = [lightgray] details = Details... edit = Edit... variables = Vars +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Name: editor.spawn = Spawn Unit diff --git a/core/assets/bundles/bundle_pl.properties b/core/assets/bundles/bundle_pl.properties index 85e19fb74e..4f782d4ea7 100644 --- a/core/assets/bundles/bundle_pl.properties +++ b/core/assets/bundles/bundle_pl.properties @@ -439,6 +439,11 @@ editor.rules = Zasady: editor.generation = Generacja: editor.objectives = Cele editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Edytuj w Grze editor.playtest = Testuj Mapę editor.publish.workshop = Opublikuj w Warsztacie @@ -495,6 +500,7 @@ editor.default = [lightgray] details = Detale... edit = Edytuj... variables = Zmienne +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Nazwa: editor.spawn = Stwórz Jednostkę diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index 10fe9465da..239bf5edb3 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -439,6 +439,11 @@ editor.rules = Regras: editor.generation = Geração: editor.objectives = Objetivos: editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Editar em jogo editor.playtest = Jogar Teste editor.publish.workshop = Publicar na oficina @@ -495,6 +500,7 @@ editor.default = [lightgray] details = Detalhes... edit = Editar... variables = Variáveis +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Nome: editor.spawn = Spawnar unidade diff --git a/core/assets/bundles/bundle_pt_PT.properties b/core/assets/bundles/bundle_pt_PT.properties index 5e0607e5c5..9b37ed9fb5 100644 --- a/core/assets/bundles/bundle_pt_PT.properties +++ b/core/assets/bundles/bundle_pt_PT.properties @@ -435,6 +435,11 @@ editor.rules = Regras: editor.generation = Geração: editor.objectives = Objectives editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Editar em jogo editor.playtest = Playtest editor.publish.workshop = Publicar na oficina @@ -490,6 +495,7 @@ editor.default = [lightgray] details = Detalhes... edit = Editar... variables = Vars +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Nome: editor.spawn = Criar unidade diff --git a/core/assets/bundles/bundle_ro.properties b/core/assets/bundles/bundle_ro.properties index a5ffd3722b..32bcc43c2b 100644 --- a/core/assets/bundles/bundle_ro.properties +++ b/core/assets/bundles/bundle_ro.properties @@ -439,6 +439,11 @@ editor.rules = Reguli: editor.generation = Generare: editor.objectives = Objectives editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Editează în Joc editor.playtest = Playtest editor.publish.workshop = Publică pe Workshop @@ -495,6 +500,7 @@ editor.default = [lightgray] details = Detalii... edit = Editează... variables = Vars +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Nume: editor.spawn = Adaugă Unitate diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index 0481a67066..d547dce469 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -439,6 +439,11 @@ editor.rules = Правила: editor.generation = Генерация: editor.objectives = Цели editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Редактировать в игре editor.playtest = Опробовать карту editor.publish.workshop = Опубликовать в Мастерской @@ -495,6 +500,7 @@ editor.default = [lightgray]<По умолчанию> details = Подробности… edit = Редактировать… variables = Переменные +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Название: editor.spawn = Создать боевую единицу diff --git a/core/assets/bundles/bundle_sr.properties b/core/assets/bundles/bundle_sr.properties index 5e0870600f..e1399dedb2 100644 --- a/core/assets/bundles/bundle_sr.properties +++ b/core/assets/bundles/bundle_sr.properties @@ -439,6 +439,11 @@ editor.rules = Pravila: editor.generation = Generisanje: editor.objectives = Zadaci editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Izmeni "U Igri" editor.playtest = Testiranje editor.publish.workshop = Objavi u Radionicu @@ -495,6 +500,7 @@ editor.default = [lightgray] details = Detalji... edit = Izmeni... variables = Varijabla +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Ime: editor.spawn = Prizovi Jedinicu diff --git a/core/assets/bundles/bundle_sv.properties b/core/assets/bundles/bundle_sv.properties index d35b73b11d..46613b6f88 100644 --- a/core/assets/bundles/bundle_sv.properties +++ b/core/assets/bundles/bundle_sv.properties @@ -435,6 +435,11 @@ editor.rules = Regler: editor.generation = Generering: editor.objectives = Objectives editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Edit In-Game editor.playtest = Playtest editor.publish.workshop = Publish On Workshop @@ -490,6 +495,7 @@ editor.default = [lightgray] details = Details... edit = Redigera... variables = Vars +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Namn: editor.spawn = Spawn Unit diff --git a/core/assets/bundles/bundle_th.properties b/core/assets/bundles/bundle_th.properties index f24076882d..9cf751f457 100644 --- a/core/assets/bundles/bundle_th.properties +++ b/core/assets/bundles/bundle_th.properties @@ -439,6 +439,11 @@ editor.rules = กฎ editor.generation = เจนเนอเรชั่น editor.objectives = เป้าหมาย editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = แก้ไขในเกม editor.playtest = เล่นทดสอบ editor.publish.workshop = เผยแพร่บนเวิร์กช็อป @@ -495,6 +500,7 @@ editor.default = [lightgray]<ค่าเริ่มต้น> details = รายละเอียด... edit = แก้ไข... variables = ตัวแปร +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = ชื่อ: editor.spawn = สร้างยูนิต diff --git a/core/assets/bundles/bundle_tk.properties b/core/assets/bundles/bundle_tk.properties index 413f9f4811..ff5af13079 100644 --- a/core/assets/bundles/bundle_tk.properties +++ b/core/assets/bundles/bundle_tk.properties @@ -435,6 +435,11 @@ editor.rules = Rules: editor.generation = Generation: editor.objectives = Objectives editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Edit In-Game editor.playtest = Playtest editor.publish.workshop = Publish On Workshop @@ -490,6 +495,7 @@ editor.default = [lightgray] details = Details... edit = Edit... variables = Vars +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = isim: editor.spawn = Spawn Unit diff --git a/core/assets/bundles/bundle_tr.properties b/core/assets/bundles/bundle_tr.properties index d124d89693..6111c94a09 100644 --- a/core/assets/bundles/bundle_tr.properties +++ b/core/assets/bundles/bundle_tr.properties @@ -439,6 +439,11 @@ editor.rules = Kurallar: editor.generation = Oluşum: editor.objectives = Görevler: editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Oyun içinde düzenle editor.playtest = Test Et editor.publish.workshop = Atölyede Yayınla @@ -495,6 +500,7 @@ editor.default = [lightgray] details = Detaylar... edit = Düzenle... variables = Değişkenler +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = İsim: editor.spawn = Birim Oluştur diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 72fbe93818..fe30ba9a16 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -441,6 +441,11 @@ editor.rules = Правила editor.generation = Генерація editor.objectives = Завдання editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Редагувати в грі editor.playtest = Протестувати в грі editor.publish.workshop = Опублікувати в Майстерні Steam @@ -497,6 +502,7 @@ editor.default = [lightgray]<За замовчуванням> details = Подробиці… edit = Змінити… variables = Змінні +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = Ім’я: editor.spawn = Створити бойову одиницю diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index ca1bc0bd32..98540e1938 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -445,6 +445,11 @@ editor.rules = Quy luật editor.generation = Tạo ra editor.objectives = Mục tiêu editor.locales = Gói ngôn ngữ +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Chỉnh sửa trong trò chơi editor.playtest = Chơi thử editor.publish.workshop = Xuất bản lên Workshop @@ -501,6 +506,7 @@ editor.default = [lightgray] details = Chi tiết... edit = Chỉnh sửa variables = Thông số +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Thông số sẵn có editor.name = Tên: editor.spawn = Đơn vị xuất hiện diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index 9553ecb6a0..f0b46c838d 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -442,6 +442,11 @@ editor.rules = 规则 editor.generation = 生成 editor.objectives = 目标 editor.locales = 本地化语言包 +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = 游戏内编辑 editor.playtest = 游戏内测试 editor.publish.workshop = 上传到创意工坊 @@ -498,6 +503,7 @@ editor.default = [lightgray]<默认> details = 详情… edit = 编辑… variables = 变量 +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = 内置变量 editor.name = 名称: editor.spawn = 生成单位 diff --git a/core/assets/bundles/bundle_zh_TW.properties b/core/assets/bundles/bundle_zh_TW.properties index 0e4ab816b4..10d0c5dacd 100644 --- a/core/assets/bundles/bundle_zh_TW.properties +++ b/core/assets/bundles/bundle_zh_TW.properties @@ -439,6 +439,11 @@ editor.rules = 規則: editor.generation = 自動生成: editor.objectives = Objectives editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = 在遊戲中編輯 editor.playtest = 測試 editor.publish.workshop = 在工作坊上發佈 @@ -495,6 +500,7 @@ editor.default = [lightgray](預設) details = 詳細資訊…… edit = 編輯…… variables = 變數 +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables editor.name = 名稱: editor.spawn = 重生單位 From 5af0654332e65d19b62553d83dba40878ef0a5d9 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 24 May 2024 23:38:19 -0400 Subject: [PATCH 209/348] Who stole the github CLI email --- .github/workflows/push.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index b64e59fe52..cd593a6dfd 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -34,7 +34,7 @@ jobs: if [ -n "$(git status --porcelain)" ]; then git config --global user.name "Github Actions" - git config --global user.email "cli@github.com" + git config --global user.email "actions@github.com" git add core/assets/bundles/* git commit -m "Automatic bundle update" git push @@ -43,7 +43,7 @@ jobs: if: ${{ github.repository == 'Anuken/Mindustry' }} run: | git config --global user.name "Github Actions" - git config --global user.email "cli@github.com" + git config --global user.email "actions@github.com" cd ../ cp -r ./Mindustry ./MindustryJitpack cd MindustryJitpack From 9ada7c0524b7635f9fa9d01ba8a39a94e22a6b28 Mon Sep 17 00:00:00 2001 From: Ettaby <89039802+Ettaby@users.noreply.github.com> Date: Sat, 25 May 2024 15:51:37 +0300 Subject: [PATCH 210/348] 2D2R (Update servers_v7.json, updated IP) (#9880) * 2D2R (Update servers_v7.json, updated IP) * 2D2R (Removed test server.) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index d54ec6c5af..4bfd0c5f0c 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -298,6 +298,6 @@ }, { "name": "2D2R", - "address": ["51.89.173.50:1284", "45.140.142.231:1045", "51.89.173.50:1432", "45.140.142.231:1052", "45.140.142.231:1040"] + "address": ["45.137.205.185:1045", "51.89.173.50:1432", "45.137.205.185:1052", "45.137.205.185:1040"] } ] From b6697d92cb3c08599b2c9f9ae2d5e30b2068db27 Mon Sep 17 00:00:00 2001 From: summoner001 Date: Sat, 25 May 2024 14:51:43 +0200 Subject: [PATCH 211/348] Update bundle_hu.properties (#9882) Translate the new lines from the "World processor list in editor" commit. --- core/assets/bundles/bundle_hu.properties | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index 3b7f3f96d2..607d4b4f6b 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -445,11 +445,11 @@ editor.rules = Szabályok editor.generation = Előállítás editor.objectives = Célok editor.locales = Helyi csomagok -editor.worldprocessors = World Processors -editor.worldprocessors.editname = Edit Name -editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. -editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? -editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. +editor.worldprocessors = Világprocesszorok +editor.worldprocessors.editname = Név szerkesztése +editor.worldprocessors.none = [lightgray]Nem találhatóak világprocesszor blokkok!\nAdj hozzá egyet a pályaszerkesztőben, vagy használd az alábbi \ue813 hozzáadás gombot. +editor.worldprocessors.nospace = Nincs szabad hely egy világprocesszor elhelyezéséhez!\nKitöltötted a pályát struktúrákkal? Miért tetted ezt? +editor.worldprocessors.delete.confirm = Biztos, hogy törölni akarod ezt a világprocesszort?\n\nHa falakkal van körülvéve, akkor egy környezeti fal fog a helyére kerülni. editor.ingame = Szerkesztés a játékban editor.playtest = Teszt a játékban editor.publish.workshop = Közzététel a Steam Műhelyben @@ -506,8 +506,9 @@ editor.default = [lightgray] details = Részletek... edit = Szerkesztés variables = Változók -logic.clear.confirm = Are you sure you want to clear all code from this processor? +logic.clear.confirm = Biztos, hogy törölni akarod az összes kódot ebből a processzorból? logic.globals = Beépített változók + editor.name = Név: editor.spawn = Egység létrehozása editor.removeunit = Egység eltávolítása From c6b1dd44f9a9271ed3c67e32d9bc5fe7229b69e0 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 25 May 2024 21:30:44 -0400 Subject: [PATCH 212/348] Server delta cap --- server/src/mindustry/server/ServerControl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/mindustry/server/ServerControl.java b/server/src/mindustry/server/ServerControl.java index 44e12c76f8..6fba4a97e7 100644 --- a/server/src/mindustry/server/ServerControl.java +++ b/server/src/mindustry/server/ServerControl.java @@ -148,7 +148,7 @@ public class ServerControl implements ApplicationListener{ return useColors ? addColors(text) : removeColors(text); }; - Time.setDeltaProvider(() -> Core.graphics.getDeltaTime() * 60f); + Time.setDeltaProvider(() -> Math.min(Core.graphics.getDeltaTime() * 60f, 10f)); registerCommands(); From a0569a63bce60137f86b0754602214fb7139ae10 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 25 May 2024 21:50:12 -0400 Subject: [PATCH 213/348] Possible entity collision variable overwrite fix --- .../src/mindustry/entities/EntityCollisions.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core/src/mindustry/entities/EntityCollisions.java b/core/src/mindustry/entities/EntityCollisions.java index 1dfc9ef86f..ca1be49af4 100644 --- a/core/src/mindustry/entities/EntityCollisions.java +++ b/core/src/mindustry/entities/EntityCollisions.java @@ -16,7 +16,7 @@ public class EntityCollisions{ //tile collisions private Vec2 vector = new Vec2(), l1 = new Vec2(); - private Rect r1 = new Rect(), r2 = new Rect(), tmp = new Rect(); + private Rect r1 = new Rect(), r2 = new Rect(), r3 = new Rect(), r4 = new Rect(), tmp = new Rect(); //entity collisions private Seq arrOut = new Seq<>(Hitboxc.class); @@ -218,12 +218,12 @@ public class EntityCollisions{ } private void updateCollision(Hitboxc solid){ - solid.hitbox(r1); - r1.x += (solid.lastX() - solid.getX()); - r1.y += (solid.lastY() - solid.getY()); + solid.hitbox(r3); + r3.x += (solid.lastX() - solid.getX()); + r3.y += (solid.lastY() - solid.getY()); - solid.hitbox(r2); - r2.merge(r1); + solid.hitbox(r4); + r4.merge(r3); arrOut.clear(); @@ -235,8 +235,8 @@ public class EntityCollisions{ for(int i = 0; i < size; i++){ Hitboxc sc = items[i]; - sc.hitbox(r1); - if(r2.overlaps(r1)){ + sc.hitbox(r3); + if(r4.overlaps(r3)){ checkCollide(solid, sc); //break out of loop when this object hits something if(!solid.isAdded()) return; From 41516dbda7fdd50d76be64af7c7e2c58392a665a Mon Sep 17 00:00:00 2001 From: prof-milki Date: Sun, 26 May 2024 04:09:29 +0200 Subject: [PATCH 214/348] Update servers_v7.json: factory server (#9868) New server instance, Europe, static IPv6-only, single game mode (for now). --- servers_v7.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/servers_v7.json b/servers_v7.json index 4bfd0c5f0c..44ccc10789 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -1,4 +1,8 @@ [ + { + "name": "Redundancy Dept", + "address": ["min7.include-once.org:8000", "min7.include-once.org:8001"] + }, { "name": "TSR Network", "address": ["de-prem-01.hosts.optikservers.com:35526", "de-prem-01.hosts.optikservers.com:35915", "de-prem-01.hosts.optikservers.com:35250", "de-prem-01.hosts.optikservers.com:27526", "de-prem-01.hosts.optikservers.com:35376", "de-prem-01.hosts.optikservers.com:35895"] From f30b24f4ace27ffdf120135ad224a9e307d43370 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 25 May 2024 22:40:37 -0400 Subject: [PATCH 215/348] Closes Anuken/Mindustry-Suggestions/issues/5012 --- core/src/mindustry/ui/Minimap.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/core/src/mindustry/ui/Minimap.java b/core/src/mindustry/ui/Minimap.java index ad828a8dd2..99c7a32303 100644 --- a/core/src/mindustry/ui/Minimap.java +++ b/core/src/mindustry/ui/Minimap.java @@ -3,9 +3,11 @@ package mindustry.ui; import arc.*; import arc.graphics.g2d.*; import arc.input.*; +import arc.math.*; import arc.scene.*; import arc.scene.event.*; import arc.scene.ui.layout.*; +import arc.util.*; import mindustry.gen.*; import static mindustry.Vars.*; @@ -20,6 +22,22 @@ public class Minimap extends Table{ add(new Element(){ { setSize(Scl.scl(140f)); + + addListener(new ClickListener(KeyCode.mouseRight){ + @Override + public void clicked(InputEvent event, float cx, float cy){ + var region = renderer.minimap.getRegion(); + if(region == null) return; + + float + sx = (cx - x) / width, + sy = (cy - y) / height, + scaledX = Mathf.lerp(region.u, region.u2, sx) * world.width() * tilesize, + scaledY = Mathf.lerp(1f - region.v2, 1f - region.v, sy) * world.height() * tilesize; + + control.input.panCamera(Tmp.v1.set(scaledX, scaledY)); + } + }); } @Override From f1f17965bcf6f723d8aa78d53b6601a4c963851d Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 26 May 2024 09:31:40 -0400 Subject: [PATCH 216/348] Fixed #9888 --- core/src/mindustry/ClientLauncher.java | 2 +- core/src/mindustry/Vars.java | 2 ++ core/src/mindustry/ai/BlockIndexer.java | 8 ++++---- core/src/mindustry/entities/comp/BuildingComp.java | 3 ++- server/src/mindustry/server/ServerControl.java | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/core/src/mindustry/ClientLauncher.java b/core/src/mindustry/ClientLauncher.java index ae8aae4ab3..2a7b133588 100644 --- a/core/src/mindustry/ClientLauncher.java +++ b/core/src/mindustry/ClientLauncher.java @@ -66,7 +66,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform Time.setDeltaProvider(() -> { float result = Core.graphics.getDeltaTime() * 60f; - return (Float.isNaN(result) || Float.isInfinite(result)) ? 1f : Mathf.clamp(result, 0.0001f, 60f / 10f); + return (Float.isNaN(result) || Float.isInfinite(result)) ? 1f : Mathf.clamp(result, 0.0001f, maxDeltaClient); }); UI.loadColors(); diff --git a/core/src/mindustry/Vars.java b/core/src/mindustry/Vars.java index 3538a39ce6..bc4c6a494a 100644 --- a/core/src/mindustry/Vars.java +++ b/core/src/mindustry/Vars.java @@ -154,6 +154,8 @@ public class Vars implements Loadable{ public static final int maxModSubtitleLength = 40; /** multicast group for discovery.*/ public static final String multicastGroup = "227.2.7.7"; + /** Maximum delta time. If the actual delta time (*60) between frames is higher than this number, the game will start to slow down. */ + public static float maxDeltaClient = 6f, maxDeltaServer = 10f; /** whether the graphical game client has loaded */ public static boolean clientLoaded = false; /** max GL texture size */ diff --git a/core/src/mindustry/ai/BlockIndexer.java b/core/src/mindustry/ai/BlockIndexer.java index 60bcca661e..7f944a3eb0 100644 --- a/core/src/mindustry/ai/BlockIndexer.java +++ b/core/src/mindustry/ai/BlockIndexer.java @@ -130,13 +130,13 @@ public class BlockIndexer{ data.turretTree.remove(build); } - //is no longer registered - build.wasDamaged = false; - //unregister damaged buildings - if(build.damaged() && damagedTiles[team.id] != null){ + if(build.wasDamaged && damagedTiles[team.id] != null){ damagedTiles[team.id].remove(build); } + + //is no longer registered + build.wasDamaged = false; } } diff --git a/core/src/mindustry/entities/comp/BuildingComp.java b/core/src/mindustry/entities/comp/BuildingComp.java index f7c114ab6d..dad6e2464b 100644 --- a/core/src/mindustry/entities/comp/BuildingComp.java +++ b/core/src/mindustry/entities/comp/BuildingComp.java @@ -1981,9 +1981,10 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, switch(prop){ case health -> { health = (float)Mathf.clamp(value, 0, maxHealth); - healthChanged(); if(health <= 0f && !dead()){ Call.buildDestroyed(self()); + }else{ + healthChanged(); } } case team -> { diff --git a/server/src/mindustry/server/ServerControl.java b/server/src/mindustry/server/ServerControl.java index 6fba4a97e7..6716c1165b 100644 --- a/server/src/mindustry/server/ServerControl.java +++ b/server/src/mindustry/server/ServerControl.java @@ -148,7 +148,7 @@ public class ServerControl implements ApplicationListener{ return useColors ? addColors(text) : removeColors(text); }; - Time.setDeltaProvider(() -> Math.min(Core.graphics.getDeltaTime() * 60f, 10f)); + Time.setDeltaProvider(() -> Math.min(Core.graphics.getDeltaTime() * 60f, maxDeltaServer)); registerCommands(); From 6243ef6d4a2c1c82e781c29b9e591f493a916ac4 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 27 May 2024 09:40:52 -0400 Subject: [PATCH 217/348] Fixed #9892 --- .../mindustry/entities/EntityCollisions.java | 18 +++++++++--------- core/src/mindustry/io/TypeIO.java | 4 ++-- core/src/mindustry/net/Packets.java | 6 +++++- .../mindustry/ui/dialogs/MapPlayDialog.java | 2 +- gradle.properties | 2 +- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/core/src/mindustry/entities/EntityCollisions.java b/core/src/mindustry/entities/EntityCollisions.java index ca1be49af4..273449bb6a 100644 --- a/core/src/mindustry/entities/EntityCollisions.java +++ b/core/src/mindustry/entities/EntityCollisions.java @@ -16,7 +16,7 @@ public class EntityCollisions{ //tile collisions private Vec2 vector = new Vec2(), l1 = new Vec2(); - private Rect r1 = new Rect(), r2 = new Rect(), r3 = new Rect(), r4 = new Rect(), tmp = new Rect(); + private Rect r1 = new Rect(), r2 = new Rect(), tmp = new Rect(); //entity collisions private Seq arrOut = new Seq<>(Hitboxc.class); @@ -218,12 +218,12 @@ public class EntityCollisions{ } private void updateCollision(Hitboxc solid){ - solid.hitbox(r3); - r3.x += (solid.lastX() - solid.getX()); - r3.y += (solid.lastY() - solid.getY()); + solid.hitbox(r1); + r1.x += (solid.lastX() - solid.getX()); + r1.y += (solid.lastY() - solid.getY()); - solid.hitbox(r4); - r4.merge(r3); + solid.hitbox(r2); + r2.merge(r1); arrOut.clear(); @@ -235,8 +235,8 @@ public class EntityCollisions{ for(int i = 0; i < size; i++){ Hitboxc sc = items[i]; - sc.hitbox(r3); - if(r4.overlaps(r3)){ + sc.hitbox(r1); + if(r2.overlaps(r1)){ checkCollide(solid, sc); //break out of loop when this object hits something if(!solid.isAdded()) return; @@ -247,4 +247,4 @@ public class EntityCollisions{ public interface SolidPred{ boolean solid(int x, int y); } -} +} \ No newline at end of file diff --git a/core/src/mindustry/io/TypeIO.java b/core/src/mindustry/io/TypeIO.java index 4a80eca3e9..de99bb0600 100644 --- a/core/src/mindustry/io/TypeIO.java +++ b/core/src/mindustry/io/TypeIO.java @@ -628,7 +628,7 @@ public class TypeIO{ } public static KickReason readKick(Reads read){ - return KickReason.values()[read.b()]; + return KickReason.all[read.b()]; } public static void writeMarkerControl(Writes write, LMarkerControl reason){ @@ -786,7 +786,7 @@ public class TypeIO{ } public static AdminAction readAction(Reads read){ - return AdminAction.values()[read.b()]; + return AdminAction.all[read.b()]; } public static void writeUnitType(Writes write, UnitType effect){ diff --git a/core/src/mindustry/net/Packets.java b/core/src/mindustry/net/Packets.java index 041ddcdb0c..c8b2ccfe4e 100644 --- a/core/src/mindustry/net/Packets.java +++ b/core/src/mindustry/net/Packets.java @@ -17,6 +17,8 @@ public class Packets{ nameInUse, idInUse, nameEmpty, customClient, serverClose, vote, typeMismatch, whitelist, playerLimit, serverRestarting; + public static final KickReason[] all = values(); + public final boolean quiet; KickReason(){ @@ -38,7 +40,9 @@ public class Packets{ } public enum AdminAction{ - kick, ban, trace, wave, switchTeam + kick, ban, trace, wave, switchTeam; + + public static final AdminAction[] all = values(); } /** Generic client connection event. */ diff --git a/core/src/mindustry/ui/dialogs/MapPlayDialog.java b/core/src/mindustry/ui/dialogs/MapPlayDialog.java index 167890f776..db72034f0b 100644 --- a/core/src/mindustry/ui/dialogs/MapPlayDialog.java +++ b/core/src/mindustry/ui/dialogs/MapPlayDialog.java @@ -102,7 +102,7 @@ public class MapPlayDialog extends BaseDialog{ ScrollPane pane = new ScrollPane(table); pane.setFadeScrollBars(false); table.row(); - for(Gamemode mode : Gamemode.values()){ + for(Gamemode mode : Gamemode.all){ if(mode.hidden) continue; table.labelWrap("[accent]" + mode + ":[] [lightgray]" + mode.description()).width(400f); table.row(); diff --git a/gradle.properties b/gradle.properties index 7dfd79eecb..dd42df65ea 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=f0d4fdbf89 +archash=eac3d7211c From 511c1870291c6322f843c4ab36acc518409f137c Mon Sep 17 00:00:00 2001 From: router Date: Mon, 27 May 2024 17:03:18 +0300 Subject: [PATCH 218/348] Update servers_v7.json (#9893) --- servers_v7.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/servers_v7.json b/servers_v7.json index 44ccc10789..4b2fd7a56c 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -303,5 +303,9 @@ { "name": "2D2R", "address": ["45.137.205.185:1045", "51.89.173.50:1432", "45.137.205.185:1052", "45.137.205.185:1040"] + }, + { + "name": "ArmyOFUkraine", + "address": ["194.247.42.131:27715"] } ] From f51c6e458d445abaa00024ff12afad5f928e13d5 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 27 May 2024 11:25:34 -0400 Subject: [PATCH 219/348] Fixed unit shields breaking on spawn --- core/src/mindustry/entities/abilities/ForceFieldAbility.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/entities/abilities/ForceFieldAbility.java b/core/src/mindustry/entities/abilities/ForceFieldAbility.java index 8ec26ff44a..545bc9af68 100644 --- a/core/src/mindustry/entities/abilities/ForceFieldAbility.java +++ b/core/src/mindustry/entities/abilities/ForceFieldAbility.java @@ -32,7 +32,7 @@ public class ForceFieldAbility extends Ability{ /** State. */ protected float radiusScale, alpha; - protected boolean wasBroken; + protected boolean wasBroken = true; private static float realRad; private static Unit paramUnit; From bf1b4f9af524b82e901eea761ea5e95400f98253 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 27 May 2024 17:23:46 -0400 Subject: [PATCH 220/348] Native file chooser, untested --- build.gradle | 2 + .../mindustry/desktop/DesktopLauncher.java | 51 +++++++++++++++++++ gradle.properties | 2 +- 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d051101a24..d68db91465 100644 --- a/build.gradle +++ b/build.gradle @@ -234,6 +234,8 @@ project(":desktop"){ dependencies{ implementation project(":core") implementation arcModule("extensions:discord") + implementation arcModule("extensions:filedialogs") + implementation arcModule("natives:natives-filedialogs") implementation arcModule("natives:natives-desktop") implementation arcModule("natives:natives-freetype-desktop") diff --git a/desktop/src/mindustry/desktop/DesktopLauncher.java b/desktop/src/mindustry/desktop/DesktopLauncher.java index 44f3b5b03f..21ba0f4e5d 100644 --- a/desktop/src/mindustry/desktop/DesktopLauncher.java +++ b/desktop/src/mindustry/desktop/DesktopLauncher.java @@ -6,6 +6,7 @@ import arc.backend.sdl.*; import arc.backend.sdl.jni.*; import arc.discord.*; import arc.discord.DiscordRPC.*; +import arc.filedialogs.*; import arc.files.*; import arc.func.*; import arc.math.*; @@ -23,6 +24,7 @@ import mindustry.net.*; import mindustry.net.Net.*; import mindustry.service.*; import mindustry.type.*; +import mindustry.ui.dialogs.*; import java.io.*; @@ -36,6 +38,55 @@ public class DesktopLauncher extends ClientLauncher{ public static void main(String[] arg){ try{ Vars.loadLogger(); + Vars.platform = new Platform(){ + @Override + public void showFileChooser(boolean open, String title, String extension, Cons cons){ + if(OS.isWindows || OS.isMac){ + Threads.daemon(() -> { + try{ + FileDialogs.loadNatives(); + + String result; + + if(open){ + result = FileDialogs.openFileDialog(title, FileChooser.getLastDirectory().absolutePath(), new String[]{"*." + extension}, "." + extension + " files", false); + }else{ + result = FileDialogs.saveFileDialog(title, FileChooser.getLastDirectory().absolutePath(), new String[]{"*." + extension}, "." + extension + " files"); + } + + if(result == null) return; + + if(result.length() > 1 && result.contains("\n")){ + result = result.split("\n")[0]; + } + + //cancelled selection, ignore result + if(result.isEmpty() || result.equals("\n")) return; + if(result.endsWith("\n")) result = result.substring(0, result.length() - 1); + if(result.contains("\n")) throw new IOException("invalid input: \"" + result + "\""); + + Fi file = Core.files.absolute(result); + Core.app.post(() -> { + FileChooser.setLastDirectory(file.isDirectory() ? file : file.parent()); + + if(!open){ + cons.get(file.parent().child(file.nameWithoutExtension() + "." + extension)); + }else{ + cons.get(file); + } + }); + }catch(Throwable error){ + Log.err("Failure to execute native file chooser", error); + Core.app.post(() -> { + Platform.super.showFileChooser(open, title, extension, cons); + }); + } + }); + }else{ + Platform.super.showFileChooser(open, title, extension, cons); + } + } + }; new SdlApplication(new DesktopLauncher(arg), new SdlConfig(){{ title = "Mindustry"; maximized = true; diff --git a/gradle.properties b/gradle.properties index dd42df65ea..1196682aac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=eac3d7211c +archash=30a01a48a1 From e7d7890891af0c6eede925e82014e3e5e5211675 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 27 May 2024 17:27:26 -0400 Subject: [PATCH 221/348] Fixes --- build.gradle | 2 +- core/src/mindustry/core/Platform.java | 43 +++++++++++++++- .../mindustry/desktop/DesktopLauncher.java | 51 ------------------- 3 files changed, 43 insertions(+), 53 deletions(-) diff --git a/build.gradle b/build.gradle index d68db91465..9bca8edb25 100644 --- a/build.gradle +++ b/build.gradle @@ -234,7 +234,6 @@ project(":desktop"){ dependencies{ implementation project(":core") implementation arcModule("extensions:discord") - implementation arcModule("extensions:filedialogs") implementation arcModule("natives:natives-filedialogs") implementation arcModule("natives:natives-desktop") implementation arcModule("natives:natives-freetype-desktop") @@ -322,6 +321,7 @@ project(":core"){ api arcModule("extensions:g3d") api arcModule("extensions:fx") api arcModule("extensions:arcnet") + implementation arcModule("extensions:filedialogs") api "com.github.Anuken:rhino:$rhinoVersion" if(localArc && debugged()) api arcModule("extensions:recorder") if(localArc) api arcModule(":extensions:packer") diff --git a/core/src/mindustry/core/Platform.java b/core/src/mindustry/core/Platform.java index 73e65615be..0cbc9869eb 100644 --- a/core/src/mindustry/core/Platform.java +++ b/core/src/mindustry/core/Platform.java @@ -1,6 +1,7 @@ package mindustry.core; import arc.*; +import arc.filedialogs.*; import arc.files.*; import arc.func.*; import arc.math.*; @@ -141,7 +142,47 @@ public interface Platform{ * @param title The title of the native dialog */ default void showFileChooser(boolean open, String title, String extension, Cons cons){ - if(OS.isLinux && !OS.isAndroid){ + if(OS.isWindows || OS.isMac){ + //native file dialog + Threads.daemon(() -> { + try{ + FileDialogs.loadNatives(); + + String result; + + if(open){ + result = FileDialogs.openFileDialog(title, FileChooser.getLastDirectory().absolutePath(), new String[]{"*." + extension}, "." + extension + " files", false); + }else{ + result = FileDialogs.saveFileDialog(title, FileChooser.getLastDirectory().absolutePath(), new String[]{"*." + extension}, "." + extension + " files"); + } + + if(result == null) return; + + if(result.length() > 1 && result.contains("\n")){ + result = result.split("\n")[0]; + } + + //cancelled selection, ignore result + if(result.isEmpty() || result.equals("\n")) return; + if(result.endsWith("\n")) result = result.substring(0, result.length() - 1); + if(result.contains("\n")) throw new IOException("invalid input: \"" + result + "\""); + + Fi file = Core.files.absolute(result); + Core.app.post(() -> { + FileChooser.setLastDirectory(file.isDirectory() ? file : file.parent()); + + if(!open){ + cons.get(file.parent().child(file.nameWithoutExtension() + "." + extension)); + }else{ + cons.get(file); + } + }); + }catch(Throwable error){ + Log.err("Failure to execute native file chooser", error); + Core.app.post(() -> defaultFileDialog(open, title, extension, cons)); + } + }); + }else if(OS.isLinux && !OS.isAndroid){ showZenity(open, title, new String[]{extension}, cons, () -> defaultFileDialog(open, title, extension, cons)); }else{ defaultFileDialog(open, title, extension, cons); diff --git a/desktop/src/mindustry/desktop/DesktopLauncher.java b/desktop/src/mindustry/desktop/DesktopLauncher.java index 21ba0f4e5d..44f3b5b03f 100644 --- a/desktop/src/mindustry/desktop/DesktopLauncher.java +++ b/desktop/src/mindustry/desktop/DesktopLauncher.java @@ -6,7 +6,6 @@ import arc.backend.sdl.*; import arc.backend.sdl.jni.*; import arc.discord.*; import arc.discord.DiscordRPC.*; -import arc.filedialogs.*; import arc.files.*; import arc.func.*; import arc.math.*; @@ -24,7 +23,6 @@ import mindustry.net.*; import mindustry.net.Net.*; import mindustry.service.*; import mindustry.type.*; -import mindustry.ui.dialogs.*; import java.io.*; @@ -38,55 +36,6 @@ public class DesktopLauncher extends ClientLauncher{ public static void main(String[] arg){ try{ Vars.loadLogger(); - Vars.platform = new Platform(){ - @Override - public void showFileChooser(boolean open, String title, String extension, Cons cons){ - if(OS.isWindows || OS.isMac){ - Threads.daemon(() -> { - try{ - FileDialogs.loadNatives(); - - String result; - - if(open){ - result = FileDialogs.openFileDialog(title, FileChooser.getLastDirectory().absolutePath(), new String[]{"*." + extension}, "." + extension + " files", false); - }else{ - result = FileDialogs.saveFileDialog(title, FileChooser.getLastDirectory().absolutePath(), new String[]{"*." + extension}, "." + extension + " files"); - } - - if(result == null) return; - - if(result.length() > 1 && result.contains("\n")){ - result = result.split("\n")[0]; - } - - //cancelled selection, ignore result - if(result.isEmpty() || result.equals("\n")) return; - if(result.endsWith("\n")) result = result.substring(0, result.length() - 1); - if(result.contains("\n")) throw new IOException("invalid input: \"" + result + "\""); - - Fi file = Core.files.absolute(result); - Core.app.post(() -> { - FileChooser.setLastDirectory(file.isDirectory() ? file : file.parent()); - - if(!open){ - cons.get(file.parent().child(file.nameWithoutExtension() + "." + extension)); - }else{ - cons.get(file); - } - }); - }catch(Throwable error){ - Log.err("Failure to execute native file chooser", error); - Core.app.post(() -> { - Platform.super.showFileChooser(open, title, extension, cons); - }); - } - }); - }else{ - Platform.super.showFileChooser(open, title, extension, cons); - } - } - }; new SdlApplication(new DesktopLauncher(arg), new SdlConfig(){{ title = "Mindustry"; maximized = true; From 9ac8d9a821618b3521909550a2692dc4089b6a62 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 27 May 2024 17:29:04 -0400 Subject: [PATCH 222/348] Fixes --- core/src/mindustry/core/Platform.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/core/Platform.java b/core/src/mindustry/core/Platform.java index 0cbc9869eb..7c2860ea84 100644 --- a/core/src/mindustry/core/Platform.java +++ b/core/src/mindustry/core/Platform.java @@ -143,6 +143,8 @@ public interface Platform{ */ default void showFileChooser(boolean open, String title, String extension, Cons cons){ if(OS.isWindows || OS.isMac){ + String formatted = (title.startsWith("@") ? Core.bundle.get(title.substring(1)) : title).replaceAll("\"", "'"); + //native file dialog Threads.daemon(() -> { try{ @@ -151,9 +153,9 @@ public interface Platform{ String result; if(open){ - result = FileDialogs.openFileDialog(title, FileChooser.getLastDirectory().absolutePath(), new String[]{"*." + extension}, "." + extension + " files", false); + result = FileDialogs.openFileDialog(formatted, FileChooser.getLastDirectory().absolutePath(), new String[]{"*." + extension}, "." + extension + " files", false); }else{ - result = FileDialogs.saveFileDialog(title, FileChooser.getLastDirectory().absolutePath(), new String[]{"*." + extension}, "." + extension + " files"); + result = FileDialogs.saveFileDialog(formatted, FileChooser.getLastDirectory().absolutePath(), new String[]{"*." + extension}, "." + extension + " files"); } if(result == null) return; From 6e7613dee31776d0990fb2fa6c1e22a7b4cf5597 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 27 May 2024 17:34:34 -0400 Subject: [PATCH 223/348] Fixes --- core/src/mindustry/core/Platform.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/core/Platform.java b/core/src/mindustry/core/Platform.java index 7c2860ea84..09b89f79a8 100644 --- a/core/src/mindustry/core/Platform.java +++ b/core/src/mindustry/core/Platform.java @@ -155,7 +155,7 @@ public interface Platform{ if(open){ result = FileDialogs.openFileDialog(formatted, FileChooser.getLastDirectory().absolutePath(), new String[]{"*." + extension}, "." + extension + " files", false); }else{ - result = FileDialogs.saveFileDialog(formatted, FileChooser.getLastDirectory().absolutePath(), new String[]{"*." + extension}, "." + extension + " files"); + result = FileDialogs.saveFileDialog(formatted, FileChooser.getLastDirectory().child("file." + extension).absolutePath(), new String[]{"*." + extension}, "." + extension + " files"); } if(result == null) return; From a260710ee6e4153663e93cfeea05f3bc2b89d37c Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 27 May 2024 17:57:34 -0400 Subject: [PATCH 224/348] MacOS file chooser fix --- core/src/mindustry/core/Platform.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/core/Platform.java b/core/src/mindustry/core/Platform.java index 09b89f79a8..77d62c6bf0 100644 --- a/core/src/mindustry/core/Platform.java +++ b/core/src/mindustry/core/Platform.java @@ -151,11 +151,13 @@ public interface Platform{ FileDialogs.loadNatives(); String result; + //on MacOS, .msav is not properly recognized until I put garbage into the array? + String[] extensions = OS.isMac && open ? new String[]{"", "*." + extension} : new String[]{"*." + extension}; if(open){ - result = FileDialogs.openFileDialog(formatted, FileChooser.getLastDirectory().absolutePath(), new String[]{"*." + extension}, "." + extension + " files", false); + result = FileDialogs.openFileDialog(formatted, FileChooser.getLastDirectory().absolutePath(), extensions, "." + extension + " files", false); }else{ - result = FileDialogs.saveFileDialog(formatted, FileChooser.getLastDirectory().child("file." + extension).absolutePath(), new String[]{"*." + extension}, "." + extension + " files"); + result = FileDialogs.saveFileDialog(formatted, FileChooser.getLastDirectory().child("file." + extension).absolutePath(), extensions, "." + extension + " files"); } if(result == null) return; From c0c68ddad1cb82a03f820d4518b051900e5bc67b Mon Sep 17 00:00:00 2001 From: Tatarinyon Date: Tue, 28 May 2024 01:01:16 +0300 Subject: [PATCH 225/348] Update servers_v7.json (#9889) * Update servers_v7.json Yes, eventually I want to add my server and I think it will be the first server added from Turkey. Currently there are only classic maps on the server. I will make maps for the server in my spare time * Update servers_v7.json --- servers_v7.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/servers_v7.json b/servers_v7.json index 4b2fd7a56c..9a29440b8a 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -304,6 +304,10 @@ "name": "2D2R", "address": ["45.137.205.185:1045", "51.89.173.50:1432", "45.137.205.185:1052", "45.137.205.185:1040"] }, + { + "name": "TRFACT", + "address": ["213.142.156.83:25565"] + }, { "name": "ArmyOFUkraine", "address": ["194.247.42.131:27715"] From 4d2163d2486ccec6610d5374dab0f21fe1d4d197 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 27 May 2024 18:12:21 -0400 Subject: [PATCH 226/348] Less frequent Erekir turret retargeting when valid target is present --- core/src/mindustry/content/Blocks.java | 7 +++++++ .../src/mindustry/world/blocks/defense/turrets/Turret.java | 7 ++++++- gradle.properties | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index d0f1cabe59..969090e462 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -4152,6 +4152,7 @@ public class Blocks{ liquidConsumed = 10f / 60f; targetInterval = 5f; + newTargetInterval = 30f; targetUnderBlocks = false; float r = range = 130f; @@ -4242,6 +4243,8 @@ public class Blocks{ shootY = 7f; rotateSpeed = 1.4f; minWarmup = 0.85f; + + newTargetInterval = 40f; shootWarmupSpeed = 0.07f; coolant = consume(new ConsumeLiquid(Liquids.water, 30f / 60f)); @@ -4462,6 +4465,8 @@ public class Blocks{ heatRequirement = 10f; maxHeatEfficiency = 2f; + newTargetInterval = 40f; + inaccuracy = 1f; shake = 2f; shootY = 4; @@ -4684,6 +4689,8 @@ public class Blocks{ shootSound = Sounds.missileLaunch; minWarmup = 0.94f; + newTargetInterval = 40f; + unitSort = UnitSorts.strongest; shootWarmupSpeed = 0.03f; targetAir = false; targetUnderBlocks = false; diff --git a/core/src/mindustry/world/blocks/defense/turrets/Turret.java b/core/src/mindustry/world/blocks/defense/turrets/Turret.java index 3245ecc6e1..7659d6102c 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/Turret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/Turret.java @@ -36,6 +36,8 @@ public class Turret extends ReloadTurret{ public final int timerTarget = timers++; /** Ticks between attempt at finding a target. */ public float targetInterval = 20; + /** Target interval for when this turret already has a valid target. -1 = targetInterval */ + public float newTargetInterval = -1f; /** Maximum ammo units stored. */ public int maxAmmo = 30; @@ -176,6 +178,7 @@ public class Turret extends ReloadTurret{ if(elevation < 0) elevation = size / 2f; if(recoilTime < 0f) recoilTime = reload; if(cooldownTime < 0f) cooldownTime = reload; + if(newTargetInterval <= 0f) newTargetInterval = targetInterval; super.init(); } @@ -405,7 +408,7 @@ public class Turret extends ReloadTurret{ if(hasAmmo()){ if(Float.isNaN(reloadCounter)) reloadCounter = 0; - if(timer(timerTarget, targetInterval)){ + if(timer(timerTarget, target == null ? newTargetInterval : targetInterval)){ findTarget(); } @@ -438,6 +441,8 @@ public class Turret extends ReloadTurret{ wasShooting = true; updateShooting(); } + }else{ + target = null; } if(alwaysShooting){ diff --git a/gradle.properties b/gradle.properties index 1196682aac..5736b39a3e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=30a01a48a1 +archash=2126b31154 From 05c2d47d51de24fedfa43accc7f928eed62216dd Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 27 May 2024 18:25:39 -0400 Subject: [PATCH 227/348] Invert --- core/src/mindustry/world/blocks/defense/turrets/Turret.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/world/blocks/defense/turrets/Turret.java b/core/src/mindustry/world/blocks/defense/turrets/Turret.java index 7659d6102c..0baea25f93 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/Turret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/Turret.java @@ -408,7 +408,7 @@ public class Turret extends ReloadTurret{ if(hasAmmo()){ if(Float.isNaN(reloadCounter)) reloadCounter = 0; - if(timer(timerTarget, target == null ? newTargetInterval : targetInterval)){ + if(timer(timerTarget, target != null ? newTargetInterval : targetInterval)){ findTarget(); } From 8944b08fbf67b3e6213cf4b21055ad0a198ebef0 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 27 May 2024 21:45:23 -0400 Subject: [PATCH 228/348] Fixed map load dialog --- core/src/mindustry/editor/MapLoadDialog.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/editor/MapLoadDialog.java b/core/src/mindustry/editor/MapLoadDialog.java index 46b98e037d..69acd8ed00 100644 --- a/core/src/mindustry/editor/MapLoadDialog.java +++ b/core/src/mindustry/editor/MapLoadDialog.java @@ -39,7 +39,7 @@ public class MapLoadDialog extends BaseDialog{ ButtonGroup

r9G~?&Nw%|?|Iu!%uRl2Kzln4$ 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

categories; public Table current; public Seq categoryNames; - public String currentName; + public String currentName = ""; public String ruleSearch = ""; public Seq additionalSetup; // for modding to easily add new rules public CustomRulesDialog(){ + this(false); + } + + public CustomRulesDialog(boolean showRuleEditRule){ super("@mode.custom"); + this.showRuleEditRule = showRuleEditRule; + loadoutDialog = new LoadoutDialog(); setFillParent(true); @@ -49,8 +57,6 @@ public class CustomRulesDialog extends BaseDialog{ additionalSetup = new Seq<>(); categories = new Seq<>(); categoryNames = new Seq<>(); - currentName = ""; - ruleSearch = ""; buttons.button("@edit", Icon.pencil, () -> { BaseDialog dialog = new BaseDialog("@waves.edit"); @@ -209,7 +215,6 @@ public class CustomRulesDialog extends BaseDialog{ main.left().defaults().fillX().left(); main.row(); - category("waves"); check("@rules.waves", b -> rules.waves = b, () -> rules.waves); check("@rules.wavesending", b -> rules.waveSending = b, () -> rules.waveSending, () -> rules.waves); @@ -352,6 +357,10 @@ public class CustomRulesDialog extends BaseDialog{ category("teams"); + //not sure where else to put this + if(showRuleEditRule){ + check("@rules.allowedit", b -> rules.allowEditRules = b, () -> rules.allowEditRules); + } team("@rules.playerteam", t -> rules.defaultTeam = t, () -> rules.defaultTeam); team("@rules.enemyteam", t -> rules.waveTeam = t, () -> rules.waveTeam); diff --git a/core/src/mindustry/ui/dialogs/MapPlayDialog.java b/core/src/mindustry/ui/dialogs/MapPlayDialog.java index db72034f0b..b2f31971b4 100644 --- a/core/src/mindustry/ui/dialogs/MapPlayDialog.java +++ b/core/src/mindustry/ui/dialogs/MapPlayDialog.java @@ -14,7 +14,7 @@ import static mindustry.Vars.*; public class MapPlayDialog extends BaseDialog{ public @Nullable Runnable playListener; - CustomRulesDialog dialog = new CustomRulesDialog(); + CustomRulesDialog dialog = new CustomRulesDialog(true); Rules rules; Gamemode selectedGamemode = Gamemode.survival; Map lastMap; diff --git a/core/src/mindustry/ui/dialogs/PausedDialog.java b/core/src/mindustry/ui/dialogs/PausedDialog.java index b854aa1eb9..677b3d53ed 100644 --- a/core/src/mindustry/ui/dialogs/PausedDialog.java +++ b/core/src/mindustry/ui/dialogs/PausedDialog.java @@ -1,7 +1,10 @@ package mindustry.ui.dialogs; import arc.*; +import arc.scene.ui.layout.*; +import mindustry.*; import mindustry.editor.*; +import mindustry.game.*; import mindustry.gen.*; import static mindustry.Vars.*; @@ -10,12 +13,28 @@ public class PausedDialog extends BaseDialog{ private MapProcessorsDialog processors = new MapProcessorsDialog(); private SaveDialog save = new SaveDialog(); private LoadDialog load = new LoadDialog(); - private boolean wasClient = false; + private CustomRulesDialog rulesDialog = new CustomRulesDialog(); public PausedDialog(){ super("@menu"); shouldPause = true; + clearChildren(); + add(titleTable).growX().row(); + + stack(cont, new Table(t -> { + t.bottom().left(); + t.button(Icon.book, () -> { + Rules toEdit = Vars.state.rules.copy(); + rulesDialog.show(toEdit, () -> state.rules.copy()); + rulesDialog.hidden(() -> { + //apply rule changes only once it is hidden + Vars.state.rules = toEdit; + Call.setRules(toEdit); + }); + }).size(70f).tooltip("@customize").visible(() -> state.rules.allowEditRules && (net.server() || !net.active())); + })).grow().row(); + shown(this::rebuild); addCloseListener(); @@ -130,7 +149,7 @@ public class PausedDialog extends BaseDialog{ } public void runExitSave(){ - wasClient = net.client(); + boolean wasClient = net.client(); if(net.client()) netClient.disconnectQuietly(); if(state.isEditor() && !wasClient){ From 2ab2b03bf8281b31efc08c771acb78f9d8880afc Mon Sep 17 00:00:00 2001 From: Github Actions Date: Mon, 24 Jun 2024 18:12:54 +0000 Subject: [PATCH 280/348] Automatic bundle update --- core/assets/bundles/bundle_be.properties | 2 ++ core/assets/bundles/bundle_bg.properties | 2 ++ core/assets/bundles/bundle_ca.properties | 2 ++ core/assets/bundles/bundle_cs.properties | 2 ++ core/assets/bundles/bundle_da.properties | 2 ++ core/assets/bundles/bundle_de.properties | 2 ++ core/assets/bundles/bundle_es.properties | 2 ++ core/assets/bundles/bundle_et.properties | 2 ++ core/assets/bundles/bundle_eu.properties | 2 ++ core/assets/bundles/bundle_fi.properties | 2 ++ core/assets/bundles/bundle_fil.properties | 2 ++ core/assets/bundles/bundle_fr.properties | 2 ++ core/assets/bundles/bundle_hu.properties | 2 ++ core/assets/bundles/bundle_id_ID.properties | 2 ++ core/assets/bundles/bundle_it.properties | 2 ++ core/assets/bundles/bundle_ja.properties | 2 ++ core/assets/bundles/bundle_ko.properties | 2 ++ core/assets/bundles/bundle_lt.properties | 2 ++ core/assets/bundles/bundle_nl.properties | 2 ++ core/assets/bundles/bundle_nl_BE.properties | 2 ++ core/assets/bundles/bundle_pl.properties | 2 ++ core/assets/bundles/bundle_pt_BR.properties | 2 ++ core/assets/bundles/bundle_pt_PT.properties | 2 ++ core/assets/bundles/bundle_ro.properties | 2 ++ core/assets/bundles/bundle_ru.properties | 2 ++ core/assets/bundles/bundle_sr.properties | 2 ++ core/assets/bundles/bundle_sv.properties | 2 ++ core/assets/bundles/bundle_th.properties | 2 ++ core/assets/bundles/bundle_tk.properties | 2 ++ core/assets/bundles/bundle_tr.properties | 2 ++ core/assets/bundles/bundle_uk_UA.properties | 2 ++ core/assets/bundles/bundle_vi.properties | 2 ++ core/assets/bundles/bundle_zh_CN.properties | 2 ++ core/assets/bundles/bundle_zh_TW.properties | 2 ++ 34 files changed, 68 insertions(+) diff --git a/core/assets/bundles/bundle_be.properties b/core/assets/bundles/bundle_be.properties index 49c165f806..27cd44b075 100644 --- a/core/assets/bundles/bundle_be.properties +++ b/core/assets/bundles/bundle_be.properties @@ -1303,6 +1303,8 @@ rules.disableworldprocessors = Адключыць Працэсары Свету rules.schematic = Схемы Дазволены rules.wavetimer = Інтэрвал хваляў rules.wavesending = Адпраўка Хваль +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Хвалі rules.airUseSpawns = Air units use spawn points rules.attack = Рэжым атакі diff --git a/core/assets/bundles/bundle_bg.properties b/core/assets/bundles/bundle_bg.properties index 16c6279320..89926a3a4f 100644 --- a/core/assets/bundles/bundle_bg.properties +++ b/core/assets/bundles/bundle_bg.properties @@ -1314,6 +1314,8 @@ rules.disableworldprocessors = Disable World Processors rules.schematic = Позволена Употребата на Схеми rules.wavetimer = Таймер за Вълни rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Вълни rules.airUseSpawns = Air units use spawn points rules.attack = Режим Атака diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index 9952d1db67..94d70b12d1 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -1317,6 +1317,8 @@ rules.disableworldprocessors = Desactiva els processadors integrats rules.schematic = Permetre l’ús d’esquemes rules.wavetimer = Temporitzador d’onades rules.wavesending = Enviament d’onades +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Onades rules.airUseSpawns = Air units use spawn points rules.attack = Mode d’atac diff --git a/core/assets/bundles/bundle_cs.properties b/core/assets/bundles/bundle_cs.properties index ef1dcf72e9..80f53b1eee 100644 --- a/core/assets/bundles/bundle_cs.properties +++ b/core/assets/bundles/bundle_cs.properties @@ -1316,6 +1316,8 @@ rules.disableworldprocessors = Zakázat Světové Procesory rules.schematic = Šablony povoleny rules.wavetimer = Časovač vln rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Vlny rules.airUseSpawns = Air units use spawn points rules.attack = Režim útoku diff --git a/core/assets/bundles/bundle_da.properties b/core/assets/bundles/bundle_da.properties index 739b32c5bf..c5c9c3d96a 100644 --- a/core/assets/bundles/bundle_da.properties +++ b/core/assets/bundles/bundle_da.properties @@ -1305,6 +1305,8 @@ rules.disableworldprocessors = Disable World Processors rules.schematic = Skabeloner tilladt rules.wavetimer = Bølge-æggeur rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Bølger rules.airUseSpawns = Air units use spawn points rules.attack = Angrebsmode diff --git a/core/assets/bundles/bundle_de.properties b/core/assets/bundles/bundle_de.properties index 1c430b02ca..3da8778462 100644 --- a/core/assets/bundles/bundle_de.properties +++ b/core/assets/bundles/bundle_de.properties @@ -1327,6 +1327,8 @@ rules.disableworldprocessors = Deaktiviere Weltprozessoren rules.schematic = Entwürfe erlaubt rules.wavetimer = Wellen-Timer rules.wavesending = Manuelle Wellen möglich +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Wellen rules.airUseSpawns = Air units use spawn points rules.attack = Angriff-Modus diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index 52107a850a..6790db4427 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -1323,6 +1323,8 @@ rules.disableworldprocessors = Desactivar procesadores estáticos rules.schematic = Permitir esquemas rules.wavetimer = Temporizador de oleadas rules.wavesending = Envío de oleadas +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Oleadas rules.airUseSpawns = Air units use spawn points rules.attack = Modo de ataque diff --git a/core/assets/bundles/bundle_et.properties b/core/assets/bundles/bundle_et.properties index 29064fb2c6..bc6d5d6271 100644 --- a/core/assets/bundles/bundle_et.properties +++ b/core/assets/bundles/bundle_et.properties @@ -1305,6 +1305,8 @@ rules.disableworldprocessors = Disable World Processors rules.schematic = Schematics Allowed rules.wavetimer = Kasuta taimerit rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Kasuta lahingulaineid rules.airUseSpawns = Air units use spawn points rules.attack = Mänguviis "Rünnak" diff --git a/core/assets/bundles/bundle_eu.properties b/core/assets/bundles/bundle_eu.properties index 9b843f1cb0..2b62fdaf39 100644 --- a/core/assets/bundles/bundle_eu.properties +++ b/core/assets/bundles/bundle_eu.properties @@ -1307,6 +1307,8 @@ rules.disableworldprocessors = Disable World Processors rules.schematic = Schematics Allowed rules.wavetimer = Boladen denboragailua rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Boladak rules.airUseSpawns = Air units use spawn points rules.attack = Eraso modua diff --git a/core/assets/bundles/bundle_fi.properties b/core/assets/bundles/bundle_fi.properties index 892b999c3c..f9b20acc36 100644 --- a/core/assets/bundles/bundle_fi.properties +++ b/core/assets/bundles/bundle_fi.properties @@ -1304,6 +1304,8 @@ rules.disableworldprocessors = Poista maailmaprosessorit käytöstä rules.schematic = Salli kaaviot rules.wavetimer = Tasojen aikaraja rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Tasot rules.airUseSpawns = Air units use spawn points rules.attack = Hyökkäystila diff --git a/core/assets/bundles/bundle_fil.properties b/core/assets/bundles/bundle_fil.properties index 5d121ba794..b4e19c1ef4 100644 --- a/core/assets/bundles/bundle_fil.properties +++ b/core/assets/bundles/bundle_fil.properties @@ -1304,6 +1304,8 @@ rules.disableworldprocessors = Disable World Processors rules.schematic = Schematics Allowed rules.wavetimer = Wave Timer rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Waves rules.airUseSpawns = Air units use spawn points rules.attack = Attack Mode diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index 96739a8150..24dab35380 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -1331,6 +1331,8 @@ rules.disableworldprocessors = Désactiver les Processeurs Globaux rules.schematic = Schémas autorisés rules.wavetimer = Compte à rebours des vagues rules.wavesending = Déclenchement des Vagues +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Vagues rules.airUseSpawns = Air units use spawn points rules.attack = Mode « Attaque » diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index 640dbb9882..d98723ce02 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -1335,6 +1335,8 @@ rules.disableworldprocessors = Világprocesszorok letiltása rules.schematic = Vázlatok engedélyezése rules.wavetimer = Hullámok időzítése rules.wavesending = Hullámok küldése +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Hullámok rules.airUseSpawns = A légi egységek használjanak kezdőpontokat rules.attack = Támadási mód diff --git a/core/assets/bundles/bundle_id_ID.properties b/core/assets/bundles/bundle_id_ID.properties index 54a78482b3..700ccf020c 100644 --- a/core/assets/bundles/bundle_id_ID.properties +++ b/core/assets/bundles/bundle_id_ID.properties @@ -1323,6 +1323,8 @@ rules.disableworldprocessors = Nonaktifkan Prosesor Dunia rules.schematic = Bagan Diperbolehkan rules.wavetimer = Pengaturan Waktu Gelombang rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Gelombang rules.airUseSpawns = Air units use spawn points rules.attack = Mode Penyerangan diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties index a01440d11e..cc3596b04e 100644 --- a/core/assets/bundles/bundle_it.properties +++ b/core/assets/bundles/bundle_it.properties @@ -1310,6 +1310,8 @@ rules.disableworldprocessors = Disabilita processori rules.schematic = Schematiche Consentite rules.wavetimer = Timer Ondate rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Ondate rules.airUseSpawns = Air units use spawn points rules.attack = Modalità Attacco diff --git a/core/assets/bundles/bundle_ja.properties b/core/assets/bundles/bundle_ja.properties index 0a9f72e68c..1f06e7e153 100644 --- a/core/assets/bundles/bundle_ja.properties +++ b/core/assets/bundles/bundle_ja.properties @@ -1316,6 +1316,8 @@ rules.disableworldprocessors = ワールドプロセッサーを無効にする rules.schematic = 設計図を許可 rules.wavetimer = ウェーブの自動進行 rules.wavesending = ウェーブスキップ +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = ウェーブ rules.airUseSpawns = Air units use spawn points rules.attack = アタックモード diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index bb4584974c..af8ee33bf3 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -1315,6 +1315,8 @@ rules.disableworldprocessors = 월드 프로세서 비활성화 rules.schematic = 설계도 허용 rules.wavetimer = 시간 제한이 있는 단계 rules.wavesending = 단계 넘김 +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = 단계 rules.airUseSpawns = Air units use spawn points rules.attack = 공격 모드 diff --git a/core/assets/bundles/bundle_lt.properties b/core/assets/bundles/bundle_lt.properties index 5c5b54f754..3f5466f876 100644 --- a/core/assets/bundles/bundle_lt.properties +++ b/core/assets/bundles/bundle_lt.properties @@ -1305,6 +1305,8 @@ rules.disableworldprocessors = Disable World Processors rules.schematic = Schematics Allowed rules.wavetimer = Bangų Laikmatis rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Bangos rules.airUseSpawns = Air units use spawn points rules.attack = Puolimo Režimas diff --git a/core/assets/bundles/bundle_nl.properties b/core/assets/bundles/bundle_nl.properties index 2f87afbe88..d7035c1049 100644 --- a/core/assets/bundles/bundle_nl.properties +++ b/core/assets/bundles/bundle_nl.properties @@ -1317,6 +1317,8 @@ rules.disableworldprocessors = Zet Wereld-Processors Uit. rules.schematic = Ontwerpen Toegestaan rules.wavetimer = Vijandelijke Golven Timer rules.wavesending = Golven Sturen +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Golven rules.airUseSpawns = Air units use spawn points rules.attack = Aanvalmodus diff --git a/core/assets/bundles/bundle_nl_BE.properties b/core/assets/bundles/bundle_nl_BE.properties index a4cee209c7..ccb5189643 100644 --- a/core/assets/bundles/bundle_nl_BE.properties +++ b/core/assets/bundles/bundle_nl_BE.properties @@ -1305,6 +1305,8 @@ rules.disableworldprocessors = Disable World Processors rules.schematic = Schematics Allowed rules.wavetimer = Wave Timer rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Waves rules.airUseSpawns = Air units use spawn points rules.attack = Attack Mode diff --git a/core/assets/bundles/bundle_pl.properties b/core/assets/bundles/bundle_pl.properties index 7e2c3d2e1e..f951e54ae8 100644 --- a/core/assets/bundles/bundle_pl.properties +++ b/core/assets/bundles/bundle_pl.properties @@ -1314,6 +1314,8 @@ rules.disableworldprocessors = Wyłącz Procesor Świata rules.schematic = Zezwalaj na schematy rules.wavetimer = Zegar Fal rules.wavesending = Wysyłanie Fal +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Fale rules.airUseSpawns = Air units use spawn points rules.attack = Tryb Ataku diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index 2009ba262a..45bf134bd6 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -1324,6 +1324,8 @@ rules.disableworldprocessors = Desativar processadores mundiais rules.schematic = Permitir Esquemas rules.wavetimer = Tempo de horda rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Hordas rules.airUseSpawns = Air units use spawn points rules.attack = Modo de ataque diff --git a/core/assets/bundles/bundle_pt_PT.properties b/core/assets/bundles/bundle_pt_PT.properties index 4dc3d7d0b4..f9dc21bc95 100644 --- a/core/assets/bundles/bundle_pt_PT.properties +++ b/core/assets/bundles/bundle_pt_PT.properties @@ -1305,6 +1305,8 @@ rules.disableworldprocessors = Disable World Processors rules.schematic = Schematics Allowed rules.wavetimer = Tempo de horda rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Hordas rules.airUseSpawns = Air units use spawn points rules.attack = Modo de ataque diff --git a/core/assets/bundles/bundle_ro.properties b/core/assets/bundles/bundle_ro.properties index 86c6afc8c9..32fd848be3 100644 --- a/core/assets/bundles/bundle_ro.properties +++ b/core/assets/bundles/bundle_ro.properties @@ -1316,6 +1316,8 @@ rules.disableworldprocessors = Disable World Processors rules.schematic = Se Pot Folosi Scheme rules.wavetimer = Valuri pe Timp rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Valuri rules.airUseSpawns = Air units use spawn points rules.attack = Modul Atac diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index f81b9b0366..51b5b032bd 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -1315,6 +1315,8 @@ rules.disableworldprocessors = Отключить мировые процесс rules.schematic = Разрешить схемы rules.wavetimer = Интервал волн rules.wavesending = Отправка волн +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Волны rules.airUseSpawns = Air units use spawn points rules.attack = Режим атаки diff --git a/core/assets/bundles/bundle_sr.properties b/core/assets/bundles/bundle_sr.properties index 1074938dc6..25866172cc 100644 --- a/core/assets/bundles/bundle_sr.properties +++ b/core/assets/bundles/bundle_sr.properties @@ -1318,6 +1318,8 @@ rules.disableworldprocessors = Onesposobi Svetovne Procesore rules.schematic = Šeme Su Dozvoljene rules.wavetimer = Talasna Štoperica rules.wavesending = Slanje Talasa +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Talasi rules.airUseSpawns = Air units use spawn points rules.attack = Mod Napada diff --git a/core/assets/bundles/bundle_sv.properties b/core/assets/bundles/bundle_sv.properties index 40463f1e96..f675d8f437 100644 --- a/core/assets/bundles/bundle_sv.properties +++ b/core/assets/bundles/bundle_sv.properties @@ -1305,6 +1305,8 @@ rules.disableworldprocessors = Disable World Processors rules.schematic = Schematics Allowed rules.wavetimer = Vågtimer rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Vågor rules.airUseSpawns = Air units use spawn points rules.attack = Attack Mode diff --git a/core/assets/bundles/bundle_th.properties b/core/assets/bundles/bundle_th.properties index 698831f965..26a7aa1fa7 100644 --- a/core/assets/bundles/bundle_th.properties +++ b/core/assets/bundles/bundle_th.properties @@ -1317,6 +1317,8 @@ rules.disableworldprocessors = ปิดการทำงานของตั rules.schematic = อนุญาตให้ใช้แผนผัง rules.wavetimer = นับถอยหลังการปล่อยคลื่น rules.wavesending = กดเพื่อปล่อยคลื่น +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = คลื่น rules.airUseSpawns = Air units use spawn points rules.attack = โหมดการโจมตี diff --git a/core/assets/bundles/bundle_tk.properties b/core/assets/bundles/bundle_tk.properties index d71712915c..bb8259dd4f 100644 --- a/core/assets/bundles/bundle_tk.properties +++ b/core/assets/bundles/bundle_tk.properties @@ -1305,6 +1305,8 @@ rules.disableworldprocessors = Disable World Processors rules.schematic = Schematics Allowed rules.wavetimer = Wave Timer rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Waves rules.airUseSpawns = Air units use spawn points rules.attack = Attack Mode diff --git a/core/assets/bundles/bundle_tr.properties b/core/assets/bundles/bundle_tr.properties index 27f034802e..a3677518d5 100644 --- a/core/assets/bundles/bundle_tr.properties +++ b/core/assets/bundles/bundle_tr.properties @@ -1314,6 +1314,8 @@ rules.disableworldprocessors = Evrensel İşlemcileri Devredışı Bırak rules.schematic = Şema Kullanılabilir rules.wavetimer = Dalga Zamanlayıcısı rules.wavesending = Dalga Gönderiliyor +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Dalgalar rules.airUseSpawns = Hava Birimleri doğuş bölgelerini kullanır rules.attack = Saldırı Modu diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index c9728190f7..079a7f9dcd 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -1325,6 +1325,8 @@ rules.disableworldprocessors = Вимкнути світові процесор rules.schematic = Використання схем дозволено rules.wavetimer = Таймер для хвиль rules.wavesending = Ручне надсилання хвиль +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Хвилі rules.airUseSpawns = Air units use spawn points rules.attack = Режим атаки diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index 3df88fe6ec..3f5fe3a96e 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -1335,6 +1335,8 @@ rules.disableworldprocessors = Vô hiệu hoá bộ xử lý thế giới rules.schematic = Cho phép dùng bản thiết kế rules.wavetimer = Đếm ngược đợt rules.wavesending = Gửi đợt +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = Đợt rules.airUseSpawns = Các đơn vị không quân dùng điểm xuất hiện rules.attack = Chế độ tấn công diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index bfbe402609..c992777b21 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -1327,6 +1327,8 @@ rules.disableworldprocessors = 禁用世界处理器 rules.schematic = 允许使用蓝图 rules.wavetimer = 波次计时器 rules.wavesending = 波次可跳波 +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = 波次 rules.airUseSpawns = Air units use spawn points rules.attack = 进攻模式 diff --git a/core/assets/bundles/bundle_zh_TW.properties b/core/assets/bundles/bundle_zh_TW.properties index 8949a4952d..85241920e8 100644 --- a/core/assets/bundles/bundle_zh_TW.properties +++ b/core/assets/bundles/bundle_zh_TW.properties @@ -1322,6 +1322,8 @@ rules.disableworldprocessors = 停用世界處理器 rules.schematic = 允許使用藍圖 rules.wavetimer = 波次時間 rules.wavesending = Wave Sending +rules.allowedit = Allow Editing Rules +rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. rules.waves = 波次 rules.airUseSpawns = Air units use spawn points rules.attack = 攻擊模式 From caf0ab581d5f26c05437ded618d506edefba11f4 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 30 Jun 2024 10:28:01 -0400 Subject: [PATCH 281/348] Item deposit cooldown rule (default: 0.5 sec) --- core/src/mindustry/entities/comp/PlayerComp.java | 1 + core/src/mindustry/game/Rules.java | 2 ++ core/src/mindustry/graphics/OverlayRenderer.java | 4 +++- core/src/mindustry/input/InputHandler.java | 11 ++++++++++- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/entities/comp/PlayerComp.java b/core/src/mindustry/entities/comp/PlayerComp.java index 50ce197306..f970cfe23b 100644 --- a/core/src/mindustry/entities/comp/PlayerComp.java +++ b/core/src/mindustry/entities/comp/PlayerComp.java @@ -47,6 +47,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra transient float deathTimer; transient String lastText = ""; transient float textFadeTime; + transient Ratekeeper itemDepositRate = new Ratekeeper(); transient private Unit lastReadUnit = Nulls.unit; transient private int wrongReadUnits; diff --git a/core/src/mindustry/game/Rules.java b/core/src/mindustry/game/Rules.java index e2850f0124..bd405c793e 100644 --- a/core/src/mindustry/game/Rules.java +++ b/core/src/mindustry/game/Rules.java @@ -107,6 +107,8 @@ public class Rules{ public boolean cleanupDeadTeams = true; /** If true, items can only be deposited in the core. */ public boolean onlyDepositCore = false; + /** Cooldown, in seconds, of item depositing for players. */ + public float itemDepositCooldown = 0.5f; /** If true, every enemy block in the radius of the (enemy) core is destroyed upon death. Used for campaign maps. */ public boolean coreDestroyClear = false; /** If true, banned blocks are hidden from the build menu. */ diff --git a/core/src/mindustry/graphics/OverlayRenderer.java b/core/src/mindustry/graphics/OverlayRenderer.java index b9dc661054..8346eb94d6 100644 --- a/core/src/mindustry/graphics/OverlayRenderer.java +++ b/core/src/mindustry/graphics/OverlayRenderer.java @@ -241,7 +241,9 @@ public class OverlayRenderer{ Draw.reset(); Building build = world.buildWorld(v.x, v.y); - if(input.canDropItem() && build != null && build.interactable(player.team()) && build.acceptStack(player.unit().item(), player.unit().stack.amount, player.unit()) > 0 && player.within(build, itemTransferRange)){ + if(input.canDropItem() && build != null && build.interactable(player.team()) && build.acceptStack(player.unit().item(), player.unit().stack.amount, player.unit()) > 0 && player.within(build, itemTransferRange) && + input.itemDepositCooldown <= 0f){ + boolean invalid = (state.rules.onlyDepositCore && !(build instanceof CoreBuild)); Lines.stroke(3f, Pal.gray); diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index b280a958be..a23af33bb1 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -83,6 +83,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ public boolean overrideLineRotation; public int rotation; public boolean droppingItem; + public float itemDepositCooldown; public Group uiGroup; public boolean isBuilding = true, buildWasAutoPaused = false, wasShooting = false; public @Nullable UnitType controlledType; @@ -142,6 +143,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ Events.on(ResetEvent.class, e -> { logicCutscene = false; + itemDepositCooldown = 0f; Arrays.fill(controlGroups, null); }); } @@ -423,6 +425,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ if(player == null || build == null || !player.within(build, itemTransferRange) || build.items == null || player.dead() || (state.rules.onlyDepositCore && !(build instanceof CoreBuild))) return; if(net.server() && (player.unit().stack.amount <= 0 || !Units.canInteract(player, build) || + //to avoid rejecting deposit packets that happen to overlap due to packet speed differences, the actual cap is double the cooldown with 2 deposits. + (!player.isLocal() && !player.itemDepositRate.allow((long)(state.rules.itemDepositCooldown * 1000 * 2), 2)) || + !netServer.admins.allowAction(player, ActionType.depositItem, build.tile, action -> { action.itemAmount = player.unit().stack.amount; action.item = player.unit().item(); @@ -796,6 +801,8 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ logicCutsceneZoom = -1f; } + itemDepositCooldown -= Time.delta / 60f; + commandBuildings.removeAll(b -> !b.isValid()); if(!commandMode){ @@ -1859,8 +1866,10 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ if(build != null && build.acceptStack(stack.item, stack.amount, player.unit()) > 0 && build.interactable(player.team()) && build.block.hasItems && player.unit().stack().amount > 0 && build.interactable(player.team())){ - if(!(state.rules.onlyDepositCore && !(build instanceof CoreBuild))){ + + if(!(state.rules.onlyDepositCore && !(build instanceof CoreBuild)) && itemDepositCooldown <= 0f){ Call.transferInventory(player, build); + itemDepositCooldown = state.rules.itemDepositCooldown; } }else{ Call.dropItem(player.angleTo(x, y)); From 04ec2c06033fca7509866c7eb489e9eee0640ffa Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 5 Jul 2024 21:42:23 -0400 Subject: [PATCH 282/348] Added toggle for always playing music --- core/assets/bundles/bundle.properties | 2 ++ core/src/mindustry/audio/SoundControl.java | 7 +++++-- core/src/mindustry/ui/dialogs/SettingsMenuDialog.java | 1 + gradle.properties | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 5b08e098ac..f58a7d3d7b 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -1136,6 +1136,8 @@ category.items = Items category.crafting = Input/Output category.function = Function category.optional = Optional Enhancements +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Skip Core Launch/Land Animation setting.landscape.name = Lock Landscape setting.shadows.name = Shadows diff --git a/core/src/mindustry/audio/SoundControl.java b/core/src/mindustry/audio/SoundControl.java index b003ebec94..432a3c05b6 100644 --- a/core/src/mindustry/audio/SoundControl.java +++ b/core/src/mindustry/audio/SoundControl.java @@ -164,8 +164,11 @@ public class SoundControl{ //this just fades out the last track to make way for ingame music silence(); - //play music at intervals - if(Time.timeSinceMillis(lastPlayed) > 1000 * musicInterval / 60f){ + if(Core.settings.getBool("alwaysmusic")){ + if(current == null){ + playRandom(); + } + }else if(Time.timeSinceMillis(lastPlayed) > 1000 * musicInterval / 60f){ //chance to play it per interval if(Mathf.chance(musicChance)){ lastPlayed = Time.millis(); diff --git a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java index 8450dfc699..01f7c05a53 100644 --- a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java +++ b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java @@ -297,6 +297,7 @@ public class SettingsMenuDialog extends BaseDialog{ } void addSettings(){ + sound.checkPref("alwaysmusic", false); sound.sliderPref("musicvol", 100, 0, 100, 1, i -> i + "%"); sound.sliderPref("sfxvol", 100, 0, 100, 1, i -> i + "%"); sound.sliderPref("ambientvol", 100, 0, 100, 1, i -> i + "%"); diff --git a/gradle.properties b/gradle.properties index 087025418f..a72d1e7346 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=7d6e89dffd +archash=b857594d11 From c91f51f8d27c7a25fcb00609a0a105ccdf1d3e29 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Sat, 6 Jul 2024 01:43:24 +0000 Subject: [PATCH 283/348] Automatic bundle update --- core/assets/bundles/bundle_be.properties | 2 ++ core/assets/bundles/bundle_bg.properties | 2 ++ core/assets/bundles/bundle_ca.properties | 2 ++ core/assets/bundles/bundle_cs.properties | 2 ++ core/assets/bundles/bundle_da.properties | 2 ++ core/assets/bundles/bundle_de.properties | 2 ++ core/assets/bundles/bundle_es.properties | 2 ++ core/assets/bundles/bundle_et.properties | 2 ++ core/assets/bundles/bundle_eu.properties | 2 ++ core/assets/bundles/bundle_fi.properties | 2 ++ core/assets/bundles/bundle_fil.properties | 2 ++ core/assets/bundles/bundle_fr.properties | 2 ++ core/assets/bundles/bundle_hu.properties | 2 ++ core/assets/bundles/bundle_id_ID.properties | 2 ++ core/assets/bundles/bundle_it.properties | 2 ++ core/assets/bundles/bundle_ja.properties | 2 ++ core/assets/bundles/bundle_ko.properties | 2 ++ core/assets/bundles/bundle_lt.properties | 2 ++ core/assets/bundles/bundle_nl.properties | 2 ++ core/assets/bundles/bundle_nl_BE.properties | 2 ++ core/assets/bundles/bundle_pl.properties | 2 ++ core/assets/bundles/bundle_pt_BR.properties | 2 ++ core/assets/bundles/bundle_pt_PT.properties | 2 ++ core/assets/bundles/bundle_ro.properties | 2 ++ core/assets/bundles/bundle_ru.properties | 2 ++ core/assets/bundles/bundle_sr.properties | 2 ++ core/assets/bundles/bundle_sv.properties | 2 ++ core/assets/bundles/bundle_th.properties | 2 ++ core/assets/bundles/bundle_tk.properties | 2 ++ core/assets/bundles/bundle_tr.properties | 2 ++ core/assets/bundles/bundle_uk_UA.properties | 2 ++ core/assets/bundles/bundle_vi.properties | 2 ++ core/assets/bundles/bundle_zh_CN.properties | 2 ++ core/assets/bundles/bundle_zh_TW.properties | 2 ++ 34 files changed, 68 insertions(+) diff --git a/core/assets/bundles/bundle_be.properties b/core/assets/bundles/bundle_be.properties index 27cd44b075..35d26ddb08 100644 --- a/core/assets/bundles/bundle_be.properties +++ b/core/assets/bundles/bundle_be.properties @@ -1107,6 +1107,8 @@ category.items = Прадметы category.crafting = Увядзенне/Выснова category.function = Функцыя category.optional = Дадатковыя паляпшэння +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Прапусціць Запуск Ядра/Анімацыю Высадкі setting.landscape.name = Толькі альбомны (гарызантальны) рэжым setting.shadows.name = Цені diff --git a/core/assets/bundles/bundle_bg.properties b/core/assets/bundles/bundle_bg.properties index 89926a3a4f..e80430fa25 100644 --- a/core/assets/bundles/bundle_bg.properties +++ b/core/assets/bundles/bundle_bg.properties @@ -1118,6 +1118,8 @@ category.items = Предмети category.crafting = Вход/Изход category.function = Функционалност category.optional = Допълнителни Подобрения +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Skip Core Launch/Land Animation setting.landscape.name = Заключване на Пейзажа setting.shadows.name = Сенки diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index 94d70b12d1..fec327393f 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -1121,6 +1121,8 @@ category.items = Elements category.crafting = Entrada/Sortida category.function = Funcionament category.optional = Millores opcionals +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Omet l’animació del llançament i aterratge del nucli setting.landscape.name = Bloca el paisatge setting.shadows.name = Ombres diff --git a/core/assets/bundles/bundle_cs.properties b/core/assets/bundles/bundle_cs.properties index 80f53b1eee..c7f2bbcc1e 100644 --- a/core/assets/bundles/bundle_cs.properties +++ b/core/assets/bundles/bundle_cs.properties @@ -1120,6 +1120,8 @@ category.items = Předměty category.crafting = Vstup/Výstup category.function = Funkce category.optional = Volitelné vylepšení +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Přeskočit Animaci Odpalu/Přístání Jádra setting.landscape.name = Uzamknout krajinu setting.shadows.name = Stíny diff --git a/core/assets/bundles/bundle_da.properties b/core/assets/bundles/bundle_da.properties index c5c9c3d96a..6eda50457c 100644 --- a/core/assets/bundles/bundle_da.properties +++ b/core/assets/bundles/bundle_da.properties @@ -1109,6 +1109,8 @@ category.items = Genstande category.crafting = Input/Output category.function = Funktion category.optional = Valgfri Opgraderinger +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Skip Core Launch/Land Animation setting.landscape.name = Lås Landskab setting.shadows.name = Skygger diff --git a/core/assets/bundles/bundle_de.properties b/core/assets/bundles/bundle_de.properties index 3da8778462..f3c1a3e1d6 100644 --- a/core/assets/bundles/bundle_de.properties +++ b/core/assets/bundles/bundle_de.properties @@ -1131,6 +1131,8 @@ category.items = Materialien category.crafting = Erzeugung category.function = Funktion category.optional = Optionale Zusätze +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Kern Start- und Lande-Animation überspringen setting.landscape.name = Querformat sperren setting.shadows.name = Schatten diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index 6790db4427..810d6a676d 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -1127,6 +1127,8 @@ category.items = Objetos category.crafting = Fabricación category.function = Función category.optional = Mejoras Opcionales +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Omitir animación de Lanzamiento/Aterrizaje del núcleo setting.landscape.name = Bloquear en horizontal setting.shadows.name = Sombras diff --git a/core/assets/bundles/bundle_et.properties b/core/assets/bundles/bundle_et.properties index bc6d5d6271..6744f93c71 100644 --- a/core/assets/bundles/bundle_et.properties +++ b/core/assets/bundles/bundle_et.properties @@ -1109,6 +1109,8 @@ category.items = Ressursid category.crafting = Sisend/Väljund category.function = Function category.optional = Valikulised täiustused +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Skip Core Launch/Land Animation setting.landscape.name = Lukusta horisontaalpaigutus setting.shadows.name = Varjud diff --git a/core/assets/bundles/bundle_eu.properties b/core/assets/bundles/bundle_eu.properties index 2b62fdaf39..c2d3f84cb3 100644 --- a/core/assets/bundles/bundle_eu.properties +++ b/core/assets/bundles/bundle_eu.properties @@ -1111,6 +1111,8 @@ category.items = Baliabideak category.crafting = Sarrera/Irteera category.function = Function category.optional = Aukerako hobekuntzak +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Skip Core Launch/Land Animation setting.landscape.name = Blokeatu horizontalean setting.shadows.name = Itzalak diff --git a/core/assets/bundles/bundle_fi.properties b/core/assets/bundles/bundle_fi.properties index f9b20acc36..8705c201f3 100644 --- a/core/assets/bundles/bundle_fi.properties +++ b/core/assets/bundles/bundle_fi.properties @@ -1108,6 +1108,8 @@ category.items = Tavarat category.crafting = Ulos/Sisääntulo category.function = Function category.optional = Mahdolliset parannukset +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Ohita ytimen laukaisun/laskeutumisen animaatio setting.landscape.name = Lukitse tasavaakaan setting.shadows.name = Varjot diff --git a/core/assets/bundles/bundle_fil.properties b/core/assets/bundles/bundle_fil.properties index b4e19c1ef4..eabd808298 100644 --- a/core/assets/bundles/bundle_fil.properties +++ b/core/assets/bundles/bundle_fil.properties @@ -1108,6 +1108,8 @@ category.items = Items category.crafting = Input/Output category.function = Function category.optional = Optional Enhancements +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Laktawan ang Core Launch/Land Animation setting.landscape.name = Lock Landscape setting.shadows.name = Shadows diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index 24dab35380..7af3543782 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -1133,6 +1133,8 @@ category.items = Objets category.crafting = Fabrication category.function = Fonction category.optional = Améliorations facultatives +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Ignorer l'animation du lancement du noyau et de l'atterrissage setting.landscape.name = Verrouiller la rotation en mode paysage setting.shadows.name = Ombres diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index d98723ce02..300a066012 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -1136,6 +1136,8 @@ category.items = Nyersanyagok category.crafting = Bemenet/kimenet category.function = Funkció category.optional = Lehetséges fejlesztések +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Támaszpont indítási/leszállási animáció kihagyása setting.landscape.name = Fekvő mód zárolása setting.shadows.name = Árnyékok diff --git a/core/assets/bundles/bundle_id_ID.properties b/core/assets/bundles/bundle_id_ID.properties index 700ccf020c..8f1f0bde9b 100644 --- a/core/assets/bundles/bundle_id_ID.properties +++ b/core/assets/bundles/bundle_id_ID.properties @@ -1127,6 +1127,8 @@ category.items = Barang category.crafting = Pemasukan/Pengeluaran category.function = Fungsi category.optional = Peningkatan Opsional +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Lewati Animasi Peluncuran/Pendaratan Inti setting.landscape.name = Kunci Pemandangan setting.shadows.name = Bayangan diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties index cc3596b04e..6aa47caa19 100644 --- a/core/assets/bundles/bundle_it.properties +++ b/core/assets/bundles/bundle_it.properties @@ -1114,6 +1114,8 @@ category.items = Oggetti category.crafting = Produzione category.function = Funzione category.optional = Miglioramenti Opzionali +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Salta il lancio del nucleo/Animazione setting.landscape.name = Visuale Orizontale setting.shadows.name = Ombre diff --git a/core/assets/bundles/bundle_ja.properties b/core/assets/bundles/bundle_ja.properties index 1f06e7e153..18c8f086ff 100644 --- a/core/assets/bundles/bundle_ja.properties +++ b/core/assets/bundles/bundle_ja.properties @@ -1120,6 +1120,8 @@ category.items = アイテム category.crafting = 搬入/搬出 category.function = 役割 category.optional = 強化オプション +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = コアの打ち上げ/着陸アニメーションをスキップ setting.landscape.name = 横画面で固定 setting.shadows.name = 影 diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index af8ee33bf3..83d24dfa40 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -1119,6 +1119,8 @@ category.items = 자원 category.crafting = 입력/출력 category.function = 기능 category.optional = 선택적 향상 +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = 코어 발사/착륙 애니메이션 건너뛰기 setting.landscape.name = 가로화면 잠금 setting.shadows.name = 그림자 diff --git a/core/assets/bundles/bundle_lt.properties b/core/assets/bundles/bundle_lt.properties index 3f5466f876..1855ed10e6 100644 --- a/core/assets/bundles/bundle_lt.properties +++ b/core/assets/bundles/bundle_lt.properties @@ -1109,6 +1109,8 @@ category.items = Daiktai category.crafting = Įeiga/Išeiga category.function = Function category.optional = Galimi Pagerinimai +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Skip Core Launch/Land Animation setting.landscape.name = Užrakinti pasukimą setting.shadows.name = Šešėliai diff --git a/core/assets/bundles/bundle_nl.properties b/core/assets/bundles/bundle_nl.properties index d7035c1049..a9565e7a58 100644 --- a/core/assets/bundles/bundle_nl.properties +++ b/core/assets/bundles/bundle_nl.properties @@ -1121,6 +1121,8 @@ category.items = Materialen category.crafting = Invoer/Uitvoer category.function = Functie category.optional = Optionele Verbeteringen +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Skip Core Lancering/Land Animatie setting.landscape.name = Vergrendel Landschap setting.shadows.name = Schaduwen diff --git a/core/assets/bundles/bundle_nl_BE.properties b/core/assets/bundles/bundle_nl_BE.properties index ccb5189643..58cfa95b77 100644 --- a/core/assets/bundles/bundle_nl_BE.properties +++ b/core/assets/bundles/bundle_nl_BE.properties @@ -1109,6 +1109,8 @@ category.items = Items category.crafting = Input/Output category.function = Function category.optional = Optional Enhancements +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Skip Core Launch/Land Animation setting.landscape.name = Lock Landscape setting.shadows.name = Shadows diff --git a/core/assets/bundles/bundle_pl.properties b/core/assets/bundles/bundle_pl.properties index f951e54ae8..a013f5d4d7 100644 --- a/core/assets/bundles/bundle_pl.properties +++ b/core/assets/bundles/bundle_pl.properties @@ -1118,6 +1118,8 @@ category.items = Przedmioty category.crafting = Przetwórstwo category.function = Funkcja category.optional = Dodatkowe ulepszenia +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Pomiń Animację Wystrzału/Lądowania setting.landscape.name = Zablokuj tryb panoramiczny setting.shadows.name = Cienie diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index 45bf134bd6..aa7a4ee556 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -1128,6 +1128,8 @@ category.items = Itens category.crafting = Entrada/Saída category.function = Função category.optional = Melhoras opcionais +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Pular animação de lançamento/pouso do Núcleo setting.landscape.name = Travar panorama setting.shadows.name = Sombras diff --git a/core/assets/bundles/bundle_pt_PT.properties b/core/assets/bundles/bundle_pt_PT.properties index f9dc21bc95..1eccbc6aba 100644 --- a/core/assets/bundles/bundle_pt_PT.properties +++ b/core/assets/bundles/bundle_pt_PT.properties @@ -1109,6 +1109,8 @@ category.items = Itens category.crafting = Construindo category.function = Function category.optional = Melhoras opcionais +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Skip Core Launch/Land Animation setting.landscape.name = Travar panorama setting.shadows.name = Sombras diff --git a/core/assets/bundles/bundle_ro.properties b/core/assets/bundles/bundle_ro.properties index 32fd848be3..f619f285bb 100644 --- a/core/assets/bundles/bundle_ro.properties +++ b/core/assets/bundles/bundle_ro.properties @@ -1120,6 +1120,8 @@ category.items = Materiale category.crafting = Necesită/Produce category.function = Funcționare category.optional = Îmbunătățiri opționale +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Sari peste Animația de Lansare/Aterizare a Nucleului setting.landscape.name = Blochează Mod Peisaj setting.shadows.name = Umbre diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index 51b5b032bd..9760beacc7 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -1120,6 +1120,8 @@ category.items = Предметы category.crafting = Ввод/вывод category.function = Действие category.optional = Дополнительные улучшения +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Пропускать анимацию запуска/приземления ядра setting.landscape.name = Только альбомный (горизонтальный) режим setting.shadows.name = Тени diff --git a/core/assets/bundles/bundle_sr.properties b/core/assets/bundles/bundle_sr.properties index 25866172cc..6bb7bb4f38 100644 --- a/core/assets/bundles/bundle_sr.properties +++ b/core/assets/bundles/bundle_sr.properties @@ -1122,6 +1122,8 @@ category.items = Resursi category.crafting = Ulaz/izlaz category.function = Funkcija category.optional = Dotatna Poboljšanja +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Preskoči animacije sletanja i poletanja Jezgra. setting.landscape.name = Zaključaj Orijentaciju setting.shadows.name = Senke diff --git a/core/assets/bundles/bundle_sv.properties b/core/assets/bundles/bundle_sv.properties index f675d8f437..ae76e627e3 100644 --- a/core/assets/bundles/bundle_sv.properties +++ b/core/assets/bundles/bundle_sv.properties @@ -1109,6 +1109,8 @@ category.items = Föremål category.crafting = Inmatning/Utmatning category.function = Function category.optional = Optional Enhancements +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Skip Core Launch/Land Animation setting.landscape.name = Lock Landscape setting.shadows.name = Skuggor diff --git a/core/assets/bundles/bundle_th.properties b/core/assets/bundles/bundle_th.properties index 26a7aa1fa7..9b50196aa5 100644 --- a/core/assets/bundles/bundle_th.properties +++ b/core/assets/bundles/bundle_th.properties @@ -1121,6 +1121,8 @@ category.items = ไอเท็ม category.crafting = การผลิต category.function = ฟังค์ชั่น category.optional = ทางเลือกการเพิ่มประสิทธิภาพ +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = ข้ามแอนิเมชั่นการบิน/ลงจอดของแกนกลาง setting.landscape.name = ล็อกภูมิทัศน์แนวนอน setting.shadows.name = เงา diff --git a/core/assets/bundles/bundle_tk.properties b/core/assets/bundles/bundle_tk.properties index bb8259dd4f..c468c30579 100644 --- a/core/assets/bundles/bundle_tk.properties +++ b/core/assets/bundles/bundle_tk.properties @@ -1109,6 +1109,8 @@ category.items = esyalar category.crafting = uretim category.function = Function category.optional = Optional Enhancements +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Skip Core Launch/Land Animation setting.landscape.name = Lock Landscape setting.shadows.name = Shadows diff --git a/core/assets/bundles/bundle_tr.properties b/core/assets/bundles/bundle_tr.properties index a3677518d5..1c1c95f1f8 100644 --- a/core/assets/bundles/bundle_tr.properties +++ b/core/assets/bundles/bundle_tr.properties @@ -1118,6 +1118,8 @@ category.items = Eşyalar category.crafting = Üretim category.function = Fonksiyon category.optional = İsteğe Bağlı Geliştirmeler +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Merkez Fırlatma/İnme Animasyonunu Atla setting.landscape.name = Yatayda sabitle setting.shadows.name = Gölgeler diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 079a7f9dcd..0d13699812 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -1129,6 +1129,8 @@ category.items = Предмети category.crafting = Виробництво category.function = Функціонал category.optional = Додаткові поліпшення +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Пропустити запуск ядра та анімацію приземлення setting.landscape.name = Тільки альбомний (горизонтальний) режим setting.shadows.name = Тіні diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index 3f5fe3a96e..7c03a68890 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -1136,6 +1136,8 @@ category.items = Vật phẩm category.crafting = Đầu vào/ra category.function = Chức năng category.optional = Cải tiến tùy chọn +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = Bỏ qua hiệu ứng phóng/đáp lõi setting.landscape.name = Khóa ngang setting.shadows.name = Đổ bóng diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index c992777b21..f5e11ba156 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -1131,6 +1131,8 @@ category.items = 物品 category.crafting = 输入/输出 category.function = 功能 category.optional = 强化(可选) +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = 跳过核心发射与着陆动画 setting.landscape.name = 锁定横屏 setting.shadows.name = 影子 diff --git a/core/assets/bundles/bundle_zh_TW.properties b/core/assets/bundles/bundle_zh_TW.properties index 85241920e8..26f5bf7575 100644 --- a/core/assets/bundles/bundle_zh_TW.properties +++ b/core/assets/bundles/bundle_zh_TW.properties @@ -1126,6 +1126,8 @@ category.items = 物品 category.crafting = 需求 category.function = 功能 category.optional = 額外的強化效果 +setting.alwaysmusic.name = Always Play Music +setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. setting.skipcoreanimation.name = 跳過核心發射/降落動畫 setting.landscape.name = 鎖定水平畫面 setting.shadows.name = 陰影 From e78bbb5fc29ba4f23a4be02c414fac29880ed6ea Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 5 Jul 2024 22:05:03 -0400 Subject: [PATCH 284/348] testing builds From 62df1e321d7d6d75ac7249965d51e3e114713a19 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 5 Jul 2024 23:35:06 -0400 Subject: [PATCH 285/348] Fixed tests --- tests/src/test/java/power/PowerTests.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/src/test/java/power/PowerTests.java b/tests/src/test/java/power/PowerTests.java index 33b452efcd..a68930655c 100644 --- a/tests/src/test/java/power/PowerTests.java +++ b/tests/src/test/java/power/PowerTests.java @@ -4,6 +4,7 @@ import arc.math.*; import arc.util.*; import mindustry.*; import mindustry.core.*; +import mindustry.gen.*; import mindustry.world.*; import mindustry.world.blocks.power.PowerGenerator.*; import mindustry.world.blocks.power.*; @@ -59,6 +60,8 @@ public class PowerTests extends PowerTestFixture{ powerGraph.add(producerTile.build); powerGraph.add(directConsumerTile.build); + for(Building build : powerGraph.all) build.updateConsumption(); + assertEquals(producedPower * Time.delta, powerGraph.getPowerProduced(), Mathf.FLOAT_ROUNDING_ERROR); assertEquals(requiredPower * Time.delta, powerGraph.getPowerNeeded(), Mathf.FLOAT_ROUNDING_ERROR); @@ -106,7 +109,10 @@ public class PowerTests extends PowerTestFixture{ powerGraph.add(batteryTile.build); + for(Building build : powerGraph.all) build.updateConsumption(); + powerGraph.update(); + assertEquals(expectedBatteryCapacity / maxCapacity, batteryTile.build.power.status, Mathf.FLOAT_ROUNDING_ERROR, parameterDescription + ": Expected battery status did not match"); if(directConsumerTile != null){ assertEquals(expectedSatisfaction, directConsumerTile.build.power.status, Mathf.FLOAT_ROUNDING_ERROR, parameterDescription + ": Satisfaction of direct consumer did not match"); From aa645fe67c886538373bff21486e213de72787a6 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 6 Jul 2024 14:57:49 -0400 Subject: [PATCH 286/348] Updated Gradle and AGP to 8 --- android/build.gradle | 13 ++++++++----- android/proguard-rules.pro | 5 +++-- android/src/mindustry/android/AndroidLauncher.java | 2 ++ gradle.properties | 1 + gradle/wrapper/gradle-wrapper.properties | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index f7f1ce377b..e73cace96d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -7,7 +7,7 @@ buildscript{ } dependencies{ - classpath 'com.android.tools.build:gradle:7.2.1' + classpath 'com.android.tools.build:gradle:8.5.0' } } @@ -29,8 +29,9 @@ task deploy(type: Copy){ } android{ - buildToolsVersion '33.0.2' - compileSdkVersion 33 + namespace = "io.anuke.mindustry" + buildToolsVersion = '34.0.0' + compileSdk = 34 sourceSets{ main{ manifest.srcFile 'AndroidManifest.xml' @@ -56,7 +57,7 @@ android{ applicationId "io.anuke.mindustry" minSdkVersion 14 - targetSdkVersion 33 + targetSdkVersion 34 versionName versionNameResult versionCode = vcode @@ -65,6 +66,8 @@ android{ props['androidBuildCode'] = (vcode + 1).toString() } props.store(file('../core/assets/version.properties').newWriter(), null) + + multiDexEnabled true } compileOptions{ @@ -72,7 +75,7 @@ android{ targetCompatibility JavaVersion.VERSION_1_8 } - flavorDimensions "google" + flavorDimensions = ["google"] signingConfigs{ release{ diff --git a/android/proguard-rules.pro b/android/proguard-rules.pro index 368b7f4e1e..ebda968ae6 100644 --- a/android/proguard-rules.pro +++ b/android/proguard-rules.pro @@ -1,11 +1,12 @@ -dontobfuscate -#these are essential packages that should not be "optimized" in any way -#the main purpose of d8 here is to shrink the absurdly-large google play games libraries -keep class mindustry.** { *; } -keep class arc.** { *; } -keep class net.jpountz.** { *; } -keep class rhino.** { *; } -keep class com.android.dex.** { *; } +-keepattributes Signature,*Annotation*,InnerClasses,EnclosingMethod + +-dontwarn javax.naming.** #-printusage out.txt \ No newline at end of file diff --git a/android/src/mindustry/android/AndroidLauncher.java b/android/src/mindustry/android/AndroidLauncher.java index fdb8a4ec56..c8175aa4f0 100644 --- a/android/src/mindustry/android/AndroidLauncher.java +++ b/android/src/mindustry/android/AndroidLauncher.java @@ -72,6 +72,8 @@ public class AndroidLauncher extends AndroidApplication{ @Override public ClassLoader loadJar(Fi jar, ClassLoader parent) throws Exception{ + //Required to load jar files in Android 14: https://developer.android.com/about/versions/14/behavior-changes-14#safer-dynamic-code-loading + jar.file().setReadOnly(); return new DexClassLoader(jar.file().getPath(), getFilesDir().getPath(), null, parent){ @Override protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException{ diff --git a/gradle.properties b/gradle.properties index a72d1e7346..a87b03065c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,5 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 +android.enableR8.fullMode=false archash=b857594d11 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3994438e22..0d1842103b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 4c93a7fad48b188e6fbf4b9d9d7f0c35a41fbbfa Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 6 Jul 2024 17:14:10 -0400 Subject: [PATCH 287/348] Removed package in manifest --- android/AndroidManifest.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 8cae20eee7..25602c4ab4 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -1,6 +1,5 @@ - + From 2b5e3ec4aafcbff0614a5136de0e5c8d47c148a8 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 7 Jul 2024 11:18:13 -0400 Subject: [PATCH 288/348] Fixed #9974 --- .../src/mindustry/world/blocks/distribution/StackConveyor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/world/blocks/distribution/StackConveyor.java b/core/src/mindustry/world/blocks/distribution/StackConveyor.java index 357c3f8705..334261712e 100644 --- a/core/src/mindustry/world/blocks/distribution/StackConveyor.java +++ b/core/src/mindustry/world/blocks/distribution/StackConveyor.java @@ -259,7 +259,8 @@ public class StackConveyor extends Block implements Autotiler{ @Override public void updateTile(){ - float eff = enabled ? (efficiency + baseEfficiency) : 0f; + //the item still needs to be "reeled" in when disabled + float eff = enabled ? (efficiency + baseEfficiency) : 1f; //reel in crater if(cooldown > 0f) cooldown = Mathf.clamp(cooldown - speed * eff * delta(), 0f, recharge); From 9772c6e95700da1e66447a2ad9f4826c53400c5d Mon Sep 17 00:00:00 2001 From: StalkerBaran <120944331+StalkerBaran@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:22:11 +0700 Subject: [PATCH 289/348] Update servers_v7.json (#9975) Add a new ~~very sus hex-like~~ server (MeowIsland) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 3f5e3c4cf1..315995bcec 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -238,7 +238,7 @@ }, { "name": "MeowIsland", - "address": ["jupiter.minerent.net:25651", "nexus.minerent.net:25596", "nexus.minerent.net:25598", "pandora.minerent.net:25620"] + "address": ["jupiter.minerent.net:25651", "nexus.minerent.net:25596", "nexus.minerent.net:25598", "pandora.minerent.net:25620", "vega.minerent.net:25635"] }, { "name": "3MIDustry", From e06803601d87517a870bd628871c313627858bf7 Mon Sep 17 00:00:00 2001 From: SITUVNgcd <44901211+SITUVNgcd@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:23:04 +0700 Subject: [PATCH 290/348] Add VNM Community server (#9980) --- servers_v7.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/servers_v7.json b/servers_v7.json index 315995bcec..2127985e3f 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -319,5 +319,9 @@ { "name": "Vndustry", "address": ["vndustry.ddns.net"] + }, + { + "name": "VNM", + "address": ["server.mindustry-tool.app"] } ] From ae268e7b2c83edc5f4db15ea4b9fc0fe8a455f77 Mon Sep 17 00:00:00 2001 From: 3MIDEV <112279985+3midev@users.noreply.github.com> Date: Sun, 7 Jul 2024 16:23:25 +0100 Subject: [PATCH 291/348] Update servers_v7.json (#10004) --- servers_v7.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/servers_v7.json b/servers_v7.json index 2127985e3f..550ad4cfd0 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -241,8 +241,8 @@ "address": ["jupiter.minerent.net:25651", "nexus.minerent.net:25596", "nexus.minerent.net:25598", "pandora.minerent.net:25620", "vega.minerent.net:25635"] }, { - "name": "3MIDustry", - "address": ["play.3midustry.octane.lol:6001", "play.3midustry.octane.lol:6002", "play.3midustry.octane.lol:6003", "play.3midustry.octane.lol:6004", "play.3midustry.octane.lol:6005", "play.3midustry.octane.lol:6006", "play.3midustry.octane.lol:6007", "play.3midustry.octane.lol:6008", "play.3midustry.octane.lol:6009", "play.3midustry.octane.lol:6010"] + "name": "Exdustry", + "address": ["uk1.noxia.cloud:6001", "uk1.noxia.cloud:6002", "uk1.noxia.cloud:6003", "uk1.noxia.cloud:6004", "uk1.noxia.cloud:6005", "uk1.noxia.cloud:6006", "uk1.noxia.cloud:6007", "uk1.noxia.cloud:6008", "uk1.noxia.cloud:6009", "uk1.noxia.cloud:6010"] }, { "name": "abcxyz remaster", From 5c10f7fc064b116241dba2dad1c246f0f8311cf8 Mon Sep 17 00:00:00 2001 From: WayZer Date: Sun, 7 Jul 2024 23:23:34 +0800 Subject: [PATCH 292/348] Update servers_be.json (#10002) --- servers_be.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/servers_be.json b/servers_be.json index 1417b44b17..6ba3d25ad2 100644 --- a/servers_be.json +++ b/servers_be.json @@ -24,5 +24,9 @@ { "name": "Darkdustry", "address": ["darkdustry.tk"] + }, + { + "name": "TinyLake", + "address": ["cn.mindustry.top:40500"] } ] From a34433c5ce4467478f8ba5829f79a6dbf89e3534 Mon Sep 17 00:00:00 2001 From: alex <67626131+alexpvpmindustry@users.noreply.github.com> Date: Sun, 7 Jul 2024 08:23:43 -0700 Subject: [PATCH 293/348] add UK servers (#9993) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 550ad4cfd0..d999dc89a1 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -258,7 +258,7 @@ }, { "name": "Alex Multiverse", - "address": ["alexmindustryv7.servegame.com:25588", "172.234.80.96:6768", "139.162.41.78:6767", "172.245.187.143:6868", "172.245.187.143:6869", "92.119.127.171:6888" ] + "address": ["alexmindustryv7.servegame.com:25588", "172.234.80.96:6768", "139.162.41.78:6767", "172.245.187.143:6868", "172.245.187.143:6869", "92.119.127.171:6888", "45.84.59.51:1025", "45.84.59.51:1027"] }, { "name": "Open PVP", From 642db09dd91668784517099197fe57de2ac51838 Mon Sep 17 00:00:00 2001 From: Vainer <57346251+Vainer-prog@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:24:24 +0700 Subject: [PATCH 294/348] Update servers_v7.json (#9989) Co-authored-by: Anuken --- servers_v7.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/servers_v7.json b/servers_v7.json index d999dc89a1..fd39dc35a9 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -320,6 +320,10 @@ "name": "Vndustry", "address": ["vndustry.ddns.net"] }, + { + "name": "ChillOut", + "address": ["87.228.8.133"] + }, { "name": "VNM", "address": ["server.mindustry-tool.app"] From 41f590d50111736fcc4f62eae6545ecb904284d6 Mon Sep 17 00:00:00 2001 From: Joan Josep Date: Sun, 7 Jul 2024 17:26:09 +0200 Subject: [PATCH 295/348] Update bundle_ca.properties (#9987) --- core/assets/bundles/bundle_ca.properties | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index fec327393f..18400fdfc6 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -1319,10 +1319,10 @@ rules.disableworldprocessors = Desactiva els processadors integrats rules.schematic = Permetre l’ús d’esquemes rules.wavetimer = Temporitzador d’onades rules.wavesending = Enviament d’onades -rules.allowedit = Allow Editing Rules -rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. +rules.allowedit = Permet editar les regles +rules.allowedit.info = Quan està activat, el jugador pot editar les regles de la partida amb el botó que hi ha a la part inferior esquerra del menú de pausa. rules.waves = Onades -rules.airUseSpawns = Air units use spawn points +rules.airUseSpawns = Les unitats aèries fan servir els punts d’aparició rules.attack = Mode d’atac rules.buildai = IA constructora de bases rules.buildaitier = Nivell de construcció de la IA @@ -2540,7 +2540,7 @@ lenum.payenter = Entra o apareix al bloc on es troba la unitat. lenum.flag = Identificador numèric de la unitat. lenum.mine = Extreu recursos en una posició. lenum.build = Construeix una estructura. -lenum.getblock = Fetch building, floor and block type at coordinates.\nUnit must be in range of the position, otherwise null is returned. +lenum.getblock = Obté el bloc, el seu tipus i el terra a les coordenades indicades.\nLa posició escollida ha d’estar a l’abast de la unitat; altrament es retornarà un valor buit. lenum.within = Comprova si la unitat està a prop d’una posició. lenum.boost = Inicia/Detén el vol. lenum.flushtext = Passa el contingut de la cua d’impressió al marcador, si es pot.\nSi s’estableix «fetch» a vertader, s’intentarà carregar les propietats de la traducció del mapa o del joc. From 20792b0253fc097eca009822292c430de02e0ee0 Mon Sep 17 00:00:00 2001 From: summoner001 Date: Sun, 7 Jul 2024 17:26:17 +0200 Subject: [PATCH 296/348] Update bundle_hu.properties (#9982) Hungaria translations for the new strings. --- core/assets/bundles/bundle_hu.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index 300a066012..0151974670 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -1337,8 +1337,8 @@ rules.disableworldprocessors = Világprocesszorok letiltása rules.schematic = Vázlatok engedélyezése rules.wavetimer = Hullámok időzítése rules.wavesending = Hullámok küldése -rules.allowedit = Allow Editing Rules -rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. +rules.allowedit = Szabályok szerkesztésének engedélyezése +rules.allowedit.info = Ha engedélyezve van, a játékos szerkesztheti a szabályokat a játékban a Szünet menü bal alsó sarkában található gomb segítségével. rules.waves = Hullámok rules.airUseSpawns = A légi egységek használjanak kezdőpontokat rules.attack = Támadási mód From 252399a7d88293311ac766a3ac635f01a90d214b Mon Sep 17 00:00:00 2001 From: SITUVNgcd <44901211+SITUVNgcd@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:26:26 +0700 Subject: [PATCH 297/348] Update Vietnamese translation (#9981) * Update Vietnamese translation - Minor fixes. - Translate new strings from https://github.com/Anuken/Mindustry/commit/2ab2b03bf8281b31efc08c771acb78f9d8880afc. * Translate new strings https://github.com/Anuken/Mindustry/commit/c91f51f8d27c7a25fcb00609a0a105ccdf1d3e29 * Fix "intervals" translation --- core/assets/bundles/bundle_vi.properties | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index 7c03a68890..cd61f3d6cb 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -1136,8 +1136,8 @@ category.items = Vật phẩm category.crafting = Đầu vào/ra category.function = Chức năng category.optional = Cải tiến tùy chọn -setting.alwaysmusic.name = Always Play Music -setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. +setting.alwaysmusic.name = Luôn phát nhạc +setting.alwaysmusic.description = Khi bật, âm nhạc sẽ luôn phát lặp lại khi chơi.\nKhi tắt, nó chỉ phát tại các khoảng thời gian ngẫu nhiên. setting.skipcoreanimation.name = Bỏ qua hiệu ứng phóng/đáp lõi setting.landscape.name = Khóa ngang setting.shadows.name = Đổ bóng @@ -1337,8 +1337,8 @@ rules.disableworldprocessors = Vô hiệu hoá bộ xử lý thế giới rules.schematic = Cho phép dùng bản thiết kế rules.wavetimer = Đếm ngược đợt rules.wavesending = Gửi đợt -rules.allowedit = Allow Editing Rules -rules.allowedit.info = When enabled, the player can edit rules in-game via the button in the bottom left corner of the Pause menu. +rules.allowedit = Cho phép sửa quy tắc +rules.allowedit.info = Khi được bật, người chơi có thể chỉnh sửa các quy tắc trong lúc chơi thông qua nút ở góc dưới bên trái của Trình đơn tạm dừng. rules.waves = Đợt rules.airUseSpawns = Các đơn vị không quân dùng điểm xuất hiện rules.attack = Chế độ tấn công @@ -1793,7 +1793,7 @@ block.red-stone-boulder.name = Tảng đá đỏ block.graphitic-wall.name = Tường than chì block.silicon-arc-furnace.name = Lò tinh luyện Silicon block.electrolyzer.name = Máy điện phân -block.atmospheric-concentrator.name = Máy tập trung khí quyển +block.atmospheric-concentrator.name = Máy ngưng tụ khí quyển block.oxidation-chamber.name = Bể Oxy hoá block.electric-heater.name = Máy nhiệt từ điện block.slag-heater.name = Máy nhiệt từ xỉ @@ -1848,8 +1848,8 @@ block.vent-condenser.name = Máy ngưng tụ hơi nước block.cliff-crusher.name = Máy nghiền vách đá block.plasma-bore.name = Khoan plasma block.large-plasma-bore.name = Khoan plasma lớn -block.impact-drill.name = Máy khoan thủy lực -block.eruption-drill.name = Máy khoan siêu thủy lực +block.impact-drill.name = Máy khoan động lực +block.eruption-drill.name = Máy khoan siêu động lực block.core-bastion.name = Lõi: Pháo đài block.core-citadel.name = Lõi: Thủ phủ block.core-acropolis.name = Lõi: Đại đô @@ -2180,7 +2180,7 @@ block.exponential-reconstructor.description = Nâng cấp đơn vị đầu vào block.tetrative-reconstructor.description = Nâng cấp đơn vị đầu vào lên cấp thứ năm và là cấp cuối cùng. block.switch.description = Công tắc có thể bật/tắt. Trạng thái có thể được đọc và điều khiển với xử lý logic. block.micro-processor.description = Chạy tập hợp các chỉ lệnh trong một vòng lặp. Có thể dùng để điều khiển đơn vị và công trình. -block.logic-processor.description = Chạy tập hợp các chỉ lệnh trong một vòng lặp. Có thể dùng để điều khiển đơn vị và công trình. Nhanh hơn bộ xử lý tiểu cấp. +block.logic-processor.description = Chạy tập hợp các chỉ lệnh trong một vòng lặp. Có thể dùng để điều khiển đơn vị và công trình. Nhanh hơn bộ xử lý vi cấp. block.hyper-processor.description = Chạy tập hợp các chỉ lệnh trong một vòng lặp. Có thể dùng để điều khiển đơn vị và công trình. Nhanh hơn bộ xử lý trung cấp. block.memory-cell.description = Lưu trữ thông tin cho bộ xử lý. block.memory-bank.description = Lưu trữ thông tin cho bộ xử lý. Dung lượng cao. From e07a739ae2c80996856be6ee01b57e951bd8c481 Mon Sep 17 00:00:00 2001 From: Ilya246 <57039557+Ilya246@users.noreply.github.com> Date: Sun, 7 Jul 2024 15:27:05 +0000 Subject: [PATCH 298/348] randomise same-playercount team assignment (#9985) --- core/src/mindustry/core/NetServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/core/NetServer.java b/core/src/mindustry/core/NetServer.java index b80f828362..6d26174479 100644 --- a/core/src/mindustry/core/NetServer.java +++ b/core/src/mindustry/core/NetServer.java @@ -59,7 +59,7 @@ public class NetServer implements ApplicationListener{ count++; } } - return count; + return (float)count + Mathf.random(-0.1f, 0.1f); //if several have the same playercount pick random }); return re == null ? null : re.team; } From 82bcf5966ebffbe6a8ba47b2928f5fe17ee2bdf0 Mon Sep 17 00:00:00 2001 From: TripleToxic <84622061+TripleToxic@users.noreply.github.com> Date: Sun, 7 Jul 2024 11:28:19 -0400 Subject: [PATCH 299/348] Sensors, SetProp, and SetRule (#9943) * some fun things for world proc only * fix the format artifact * SetProp and reformating * Wildcard imports * Wildcard imports * velocity conversion * compacting description Co-authored-by: Anuken * Update core/src/mindustry/world/blocks/defense/turrets/Turret.java --------- Co-authored-by: Anuken --- core/assets/bundles/bundle.properties | 1 + core/src/mindustry/entities/comp/UnitComp.java | 4 ++++ core/src/mindustry/logic/LAccess.java | 6 ++++-- core/src/mindustry/logic/LExecutor.java | 1 + core/src/mindustry/logic/LogicRule.java | 1 + .../blocks/defense/turrets/ContinuousLiquidTurret.java | 9 +++++++++ .../world/blocks/defense/turrets/ItemTurret.java | 9 +++++++++ .../world/blocks/defense/turrets/LiquidTurret.java | 9 +++++++++ 8 files changed, 38 insertions(+), 2 deletions(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index f58a7d3d7b..fd1b5e118d 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -2438,6 +2438,7 @@ lenum.shootp = Shoot at a unit/building with velocity prediction. lenum.config = Building configuration, e.g. sorter item. lenum.enabled = Whether the block is enabled. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Illuminator color. laccess.controller = Unit controller. If processor controlled, returns processor.\nOtherwise, returns the unit itself. laccess.dead = Whether a unit/building is dead or no longer valid. diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index 1b4e095909..63f30a4fc8 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -214,6 +214,8 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I case ammoCapacity -> type.ammoCapacity; case x -> World.conv(x); case y -> World.conv(y); + case velocityX -> vel.x * 60f / tilesize; + case velocityY -> vel.y * 60f / tilesize; case dead -> dead || !isAdded() ? 1 : 0; case team -> team.id; case shooting -> isShooting() ? 1 : 0; @@ -282,6 +284,8 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I y = World.unconv((float)value); if(!isLocal()) snapInterpolation(); } + case velocityX -> vel.x = (float)(value * tilesize / 60d); + case velocityY -> vel.y = (float)(value * tilesize / 60d); case rotation -> rotation = (float)value; case team -> { if(!net.client()){ diff --git a/core/src/mindustry/logic/LAccess.java b/core/src/mindustry/logic/LAccess.java index 46044562d6..a4d242e27c 100644 --- a/core/src/mindustry/logic/LAccess.java +++ b/core/src/mindustry/logic/LAccess.java @@ -17,6 +17,7 @@ public enum LAccess{ powerNetOut, ammo, ammoCapacity, + currentAmmoType, health, maxHealth, heat, @@ -28,6 +29,8 @@ public enum LAccess{ rotation, x, y, + velocityX, + velocityY, shootX, shootY, cameraX, @@ -68,7 +71,7 @@ public enum LAccess{ all = values(), senseable = Seq.select(all, t -> t.params.length <= 1).toArray(LAccess.class), controls = Seq.select(all, t -> t.params.length > 0).toArray(LAccess.class), - settable = {x, y, rotation, speed, armor, health, shield, team, flag, totalPower, payloadType}; + settable = {x, y, velocityX, velocityY, rotation, speed, armor, health, shield, team, flag, totalPower, payloadType}; LAccess(String... params){ this.params = params; @@ -79,5 +82,4 @@ public enum LAccess{ this.params = params; isObj = obj; } - } diff --git a/core/src/mindustry/logic/LExecutor.java b/core/src/mindustry/logic/LExecutor.java index f06cac9e1e..8ccfd4627f 100644 --- a/core/src/mindustry/logic/LExecutor.java +++ b/core/src/mindustry/logic/LExecutor.java @@ -1497,6 +1497,7 @@ public class LExecutor{ } case ambientLight -> state.rules.ambientLight.fromDouble(value.num()); case solarMultiplier -> state.rules.solarMultiplier = Math.max(value.numf(), 0f); + case dragMultiplier -> state.rules.dragMultiplier = Math.max(value.numf(), 0f); case ban -> { Object cont = value.obj(); if(cont instanceof Block b){ diff --git a/core/src/mindustry/logic/LogicRule.java b/core/src/mindustry/logic/LogicRule.java index 0c05fcd033..6ce8de4b35 100644 --- a/core/src/mindustry/logic/LogicRule.java +++ b/core/src/mindustry/logic/LogicRule.java @@ -15,6 +15,7 @@ public enum LogicRule{ lighting, ambientLight, solarMultiplier, + dragMultiplier, ban, unban, diff --git a/core/src/mindustry/world/blocks/defense/turrets/ContinuousLiquidTurret.java b/core/src/mindustry/world/blocks/defense/turrets/ContinuousLiquidTurret.java index a843a3e985..ffd50d5b3d 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/ContinuousLiquidTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/ContinuousLiquidTurret.java @@ -4,6 +4,7 @@ import arc.struct.*; import mindustry.content.*; import mindustry.entities.bullet.*; import mindustry.gen.*; +import mindustry.logic.*; import mindustry.type.*; import mindustry.world.consumers.*; import mindustry.world.meta.*; @@ -77,6 +78,14 @@ public class ContinuousLiquidTurret extends ContinuousTurret{ super.updateTile(); } + @Override + public Object senseObject(LAccess sensor){ + return switch(sensor){ + case currentAmmoType -> liquids.current(); + default -> super.senseObject(sensor); + }; + } + @Override public boolean canConsume(){ return hasCorrectAmmo() && super.canConsume(); diff --git a/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java b/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java index 93a77b644f..51c07de96f 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java @@ -11,6 +11,7 @@ import mindustry.entities.bullet.*; import mindustry.game.EventType.*; import mindustry.gen.*; import mindustry.graphics.*; +import mindustry.logic.*; import mindustry.type.*; import mindustry.ui.*; import mindustry.world.consumers.*; @@ -107,6 +108,14 @@ public class ItemTurret extends Turret{ } } + @Override + public Object senseObject(LAccess sensor){ + return switch(sensor){ + case currentAmmoType -> ammo.size > 0 ? ((ItemEntry)ammo.peek()).item : null; + default -> super.senseObject(sensor); + }; + } + @Override public void updateTile(){ unit.ammo((float)unit.type().ammoCapacity * totalAmmo / maxAmmo); diff --git a/core/src/mindustry/world/blocks/defense/turrets/LiquidTurret.java b/core/src/mindustry/world/blocks/defense/turrets/LiquidTurret.java index c0087eee5f..82a9cf6d9e 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/LiquidTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/LiquidTurret.java @@ -6,6 +6,7 @@ import mindustry.core.*; import mindustry.entities.*; import mindustry.entities.bullet.*; import mindustry.gen.*; +import mindustry.logic.*; import mindustry.type.*; import mindustry.world.*; import mindustry.world.consumers.*; @@ -72,6 +73,14 @@ public class LiquidTurret extends Turret{ super.updateTile(); } + @Override + public Object senseObject(LAccess sensor){ + return switch(sensor){ + case currentAmmoType -> liquids.current(); + default -> super.senseObject(sensor); + }; + } + @Override protected void findTarget(){ if(extinguish && liquids.current().canExtinguish()){ From 41aea39a80ff8d9cf56e9af132e2eed5b435323c Mon Sep 17 00:00:00 2001 From: Github Actions Date: Sun, 7 Jul 2024 15:29:11 +0000 Subject: [PATCH 300/348] Automatic bundle update --- core/assets/bundles/bundle_be.properties | 1 + core/assets/bundles/bundle_bg.properties | 1 + core/assets/bundles/bundle_ca.properties | 1 + core/assets/bundles/bundle_cs.properties | 1 + core/assets/bundles/bundle_da.properties | 1 + core/assets/bundles/bundle_de.properties | 1 + core/assets/bundles/bundle_es.properties | 1 + core/assets/bundles/bundle_et.properties | 1 + core/assets/bundles/bundle_eu.properties | 1 + core/assets/bundles/bundle_fi.properties | 1 + core/assets/bundles/bundle_fil.properties | 1 + core/assets/bundles/bundle_fr.properties | 1 + core/assets/bundles/bundle_hu.properties | 1 + core/assets/bundles/bundle_id_ID.properties | 1 + core/assets/bundles/bundle_it.properties | 1 + core/assets/bundles/bundle_ja.properties | 1 + core/assets/bundles/bundle_ko.properties | 1 + core/assets/bundles/bundle_lt.properties | 1 + core/assets/bundles/bundle_nl.properties | 1 + core/assets/bundles/bundle_nl_BE.properties | 1 + core/assets/bundles/bundle_pl.properties | 1 + core/assets/bundles/bundle_pt_BR.properties | 1 + core/assets/bundles/bundle_pt_PT.properties | 1 + core/assets/bundles/bundle_ro.properties | 1 + core/assets/bundles/bundle_ru.properties | 1 + core/assets/bundles/bundle_sr.properties | 1 + core/assets/bundles/bundle_sv.properties | 1 + core/assets/bundles/bundle_th.properties | 1 + core/assets/bundles/bundle_tk.properties | 1 + core/assets/bundles/bundle_tr.properties | 1 + core/assets/bundles/bundle_uk_UA.properties | 1 + core/assets/bundles/bundle_vi.properties | 1 + core/assets/bundles/bundle_zh_CN.properties | 1 + core/assets/bundles/bundle_zh_TW.properties | 1 + 34 files changed, 34 insertions(+) diff --git a/core/assets/bundles/bundle_be.properties b/core/assets/bundles/bundle_be.properties index 35d26ddb08..442b6afcdd 100644 --- a/core/assets/bundles/bundle_be.properties +++ b/core/assets/bundles/bundle_be.properties @@ -2374,6 +2374,7 @@ lenum.shoot = Shoot at a position. lenum.shootp = Shoot at a unit/building with velocity prediction. lenum.config = Building configuration, e.g. sorter item. lenum.enabled = Whether the block is enabled. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Illuminator color. laccess.controller = Unit controller. If processor controlled, returns processor.\nIf in a formation, returns leader.\nOtherwise, returns the unit itself. laccess.dead = Whether a unit/building is dead or no longer valid. diff --git a/core/assets/bundles/bundle_bg.properties b/core/assets/bundles/bundle_bg.properties index e80430fa25..7e895bada1 100644 --- a/core/assets/bundles/bundle_bg.properties +++ b/core/assets/bundles/bundle_bg.properties @@ -2390,6 +2390,7 @@ lenum.shoot = Стреля към позиция. lenum.shootp = Прицелва се в единица/сграда, изчислявайки нейната скорост. lenum.config = Building configuration, e.g. sorter item. lenum.enabled = Дали блокът е активиран или забранен. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Цвят на осветителя. laccess.controller = Връща кой контролира единицата.\nАко е управляване от процесор, връща процесора.\nАко е във формация, връща лидера.\nИначе, връща самата единица. diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index 18400fdfc6..bcee969240 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -2400,6 +2400,7 @@ lenum.shoot = Dispara a una posició. lenum.shootp = Dispara a una unitat/bloc tenint en compte la seva velocitat a l’hora d’apuntar. lenum.config = Configuració de l’estructura, com ara el classificador. lenum.enabled = Retorna si el bloc està activat. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Color de l’il·luminador. laccess.controller = Controlador de la unitat. Si es controla per processador, retorna el processador.\nAltrament, retorna la mateixa unitat. diff --git a/core/assets/bundles/bundle_cs.properties b/core/assets/bundles/bundle_cs.properties index c7f2bbcc1e..66acc27401 100644 --- a/core/assets/bundles/bundle_cs.properties +++ b/core/assets/bundles/bundle_cs.properties @@ -2395,6 +2395,7 @@ lenum.shoot = Vystřelí na určitou pozici. lenum.shootp = Vystřelí na jednotku/budovu s rychlostní předpovědí. lenum.config = Konfigurace budovy, např. třídící věc pro třídičku. lenum.enabled = Zda je blok povolen. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Barva osvětlovače. laccess.controller = Kontroler jednotky. Pokud procesor je kontrolován, vrátí procesor\nPokud je ve formaci, vrací vůdce.\nJinak vrací jednotku. diff --git a/core/assets/bundles/bundle_da.properties b/core/assets/bundles/bundle_da.properties index 6eda50457c..caac178e24 100644 --- a/core/assets/bundles/bundle_da.properties +++ b/core/assets/bundles/bundle_da.properties @@ -2374,6 +2374,7 @@ lenum.shoot = Shoot at a position. lenum.shootp = Shoot at a unit/building with velocity prediction. lenum.config = Building configuration, e.g. sorter item. lenum.enabled = Whether the block is enabled. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Illuminator color. laccess.controller = Unit controller. If processor controlled, returns processor.\nIf in a formation, returns leader.\nOtherwise, returns the unit itself. laccess.dead = Whether a unit/building is dead or no longer valid. diff --git a/core/assets/bundles/bundle_de.properties b/core/assets/bundles/bundle_de.properties index f3c1a3e1d6..6bd88461d2 100644 --- a/core/assets/bundles/bundle_de.properties +++ b/core/assets/bundles/bundle_de.properties @@ -2425,6 +2425,7 @@ lenum.shoot = Schießt auf eine Position. lenum.shootp = Schießt auf eine Einheit / einen Block und sagt deren Position voraus. lenum.config = Blockkonfiguration, z.B. das ausgewählte Item in einem Sortierer. lenum.enabled = Ob der Block an oder aus ist. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Illuminiererfarbe. laccess.controller = Einheitensteurer. Gibt "processor" zurück, wenn die Einheit prozessorgesteuert ist,.\nGibt den Steuerer zurück, wenn die Einheit Teil einer Formation ist.\nSonst wird einfach die Einheit zurückgegeben. diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index 810d6a676d..93a2f0f3a4 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -2418,6 +2418,7 @@ lenum.shoot = Dispara a una posición. lenum.shootp = Dispara a una unidad/estructura con predicción de velocidad. lenum.config = Configuración de estructura, por ejemplo: el objeto seleccionado en un clasificador. lenum.enabled = Si el bloque está activado. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Color del iluminador. laccess.controller = Controlador de unidad. Si se controla mediante un procesador, devuelve dicho procesador.\nSi está en formación, devuelve su líder.\nDe otra forma, devuelve la misma unidad. diff --git a/core/assets/bundles/bundle_et.properties b/core/assets/bundles/bundle_et.properties index 6744f93c71..f7fd198ab3 100644 --- a/core/assets/bundles/bundle_et.properties +++ b/core/assets/bundles/bundle_et.properties @@ -2376,6 +2376,7 @@ lenum.shoot = Shoot at a position. lenum.shootp = Shoot at a unit/building with velocity prediction. lenum.config = Building configuration, e.g. sorter item. lenum.enabled = Whether the block is enabled. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Illuminator color. laccess.controller = Unit controller. If processor controlled, returns processor.\nIf in a formation, returns leader.\nOtherwise, returns the unit itself. laccess.dead = Whether a unit/building is dead or no longer valid. diff --git a/core/assets/bundles/bundle_eu.properties b/core/assets/bundles/bundle_eu.properties index c2d3f84cb3..c2f4e97364 100644 --- a/core/assets/bundles/bundle_eu.properties +++ b/core/assets/bundles/bundle_eu.properties @@ -2378,6 +2378,7 @@ lenum.shoot = Shoot at a position. lenum.shootp = Shoot at a unit/building with velocity prediction. lenum.config = Building configuration, e.g. sorter item. lenum.enabled = Whether the block is enabled. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Illuminator color. laccess.controller = Unit controller. If processor controlled, returns processor.\nIf in a formation, returns leader.\nOtherwise, returns the unit itself. laccess.dead = Whether a unit/building is dead or no longer valid. diff --git a/core/assets/bundles/bundle_fi.properties b/core/assets/bundles/bundle_fi.properties index 8705c201f3..06da2a8ea5 100644 --- a/core/assets/bundles/bundle_fi.properties +++ b/core/assets/bundles/bundle_fi.properties @@ -2379,6 +2379,7 @@ lenum.shoot = Ammu tiettyä sijaintia. lenum.shootp = Ammu yksikköä/rakennusta nopeudenennustus päällä. lenum.config = Rakennuksen säätö, esim. lajittelijan valinta. lenum.enabled = Selvitä, onko palikka päällä. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Lampun väri. laccess.controller = Yksikön hallitsija. Jos yksikköä hallitsee prosessori, palauttaa prosessorin.\nJos yksikkö on muodostelmassa, palauttaa johtajan.\nPalauttaa muulloin itse yksikön. laccess.dead = Selvitä, onko yksikkö/rakennus tuhoutunut tai ei enää kelvollinen. diff --git a/core/assets/bundles/bundle_fil.properties b/core/assets/bundles/bundle_fil.properties index eabd808298..466a2dd69e 100644 --- a/core/assets/bundles/bundle_fil.properties +++ b/core/assets/bundles/bundle_fil.properties @@ -2375,6 +2375,7 @@ lenum.shoot = Shoot at a position. lenum.shootp = Shoot at a unit/building with velocity prediction. lenum.config = Building configuration, e.g. sorter item. lenum.enabled = Whether the block is enabled. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Illuminator color. laccess.controller = Unit controller. If processor controlled, returns processor.\nIf in a formation, returns leader.\nOtherwise, returns the unit itself. laccess.dead = Whether a unit/building is dead or no longer valid. diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index 7af3543782..8c57d0bb1e 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -2425,6 +2425,7 @@ lenum.shoot = Tire à une position donnée. lenum.shootp = Tire à une unité/bâtiment avec la prédiction de mouvement. lenum.config = La configuration d'un bâtiment. Par exemple, l'objet sélectionné dans un trieur. lenum.enabled = Retourne si le bloc est activé ou pas. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = La couleur d'un illuminateur. laccess.controller = Le contrôleur de l'Unité.\nSi l'Unité est contrôlée par un processeur, cela retournera le processeur en question.\nSi l'Unité est en formation, cela retournera le leader de la formation.\nSinon, renvoie l’unité elle-même. diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index 0151974670..5d4de8ef2c 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -2437,6 +2437,7 @@ lenum.shoot = Lövés egy adott pontra. lenum.shootp = Lövés egy egységre/épületre sebesség-előrejelzéssel. lenum.config = Épületkonfiguráció, például nyersanyag-válogató. lenum.enabled = Engedélyezve van-e a blokk. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Megvilágítás színe. laccess.controller = Egységvezérlő. Ha processzor vezérli, akkor a processzort adja vissza.\nKülönben magát az egységet adja vissza. diff --git a/core/assets/bundles/bundle_id_ID.properties b/core/assets/bundles/bundle_id_ID.properties index 8f1f0bde9b..9c058872ca 100644 --- a/core/assets/bundles/bundle_id_ID.properties +++ b/core/assets/bundles/bundle_id_ID.properties @@ -2416,6 +2416,7 @@ lenum.shoot = Menembak pada suatu posisi yang ditentukan. lenum.shootp = Menembak pada unit/bangunan dengan prediksi kecepatan. lenum.config = Pengaturan bangunan, misalnya menyortir barang. lenum.enabled = Menentukan aktif tidaknya suatu blok. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Warna lampu. laccess.controller = Pengendali unit. Jika dikendalikan prosesor, mengembalikan prosesor.\nJika unit dalam barisan, mengembalikan leader.\nSebaliknya, mengembalikan unit itu sendiri. diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties index 6aa47caa19..5b6dc2407d 100644 --- a/core/assets/bundles/bundle_it.properties +++ b/core/assets/bundles/bundle_it.properties @@ -2388,6 +2388,7 @@ lenum.shoot = Shoot at a position. lenum.shootp = Shoot at a unit/building with velocity prediction. lenum.config = Building configuration, e.g. sorter item. lenum.enabled = Whether the block is enabled. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Illuminator color. laccess.controller = Unit controller. If processor controlled, returns processor.\nIf in a formation, returns leader.\nOtherwise, returns the unit itself. laccess.dead = Whether a unit/building is dead or no longer valid. diff --git a/core/assets/bundles/bundle_ja.properties b/core/assets/bundles/bundle_ja.properties index 18c8f086ff..ab9b095823 100644 --- a/core/assets/bundles/bundle_ja.properties +++ b/core/assets/bundles/bundle_ja.properties @@ -2392,6 +2392,7 @@ lenum.shoot = 指定した座標に向かって撃ちます。 lenum.shootp = 任意のユニットや建物を撃ちます。 lenum.config = 建物の設定を取得します。\n例:ソーターに設定されているアイテムなど lenum.enabled = ブロックが有効かどうかを取得します。 +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = イルミネーターの色を取得します。 laccess.controller = ユニットを制御しているものを取得します。\nプロセッサ制御の場合、制御しているプロセッサを返します。\nほかのユニットに制御されている場合、制御しているユニットを返します。\nそれ以外の場合は、ユニット自身を返します。 laccess.dead = ユニットや建物が機能しているかどうか、またはもう有効でないかどうか。 diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index 83d24dfa40..79f23ca5ab 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -2393,6 +2393,7 @@ lenum.shoot = 특정 위치에 발사 lenum.shootp = 목표물 속도를 예측하여 발사 lenum.config = 필터의 아이템같은 건물의 설정 lenum.enabled = 블록의 활성 여부 +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = 조명 색상 laccess.controller = 기체 제어자. 프로세서가 제어하면, 프로세서를 반환합니다.\n다른 기체에 의해 지휘되면(G키), 지휘하는 기체를 반환합니다.\n그 외에는 자신을 반환합니다. diff --git a/core/assets/bundles/bundle_lt.properties b/core/assets/bundles/bundle_lt.properties index 1855ed10e6..d197bd3657 100644 --- a/core/assets/bundles/bundle_lt.properties +++ b/core/assets/bundles/bundle_lt.properties @@ -2376,6 +2376,7 @@ lenum.shoot = Shoot at a position. lenum.shootp = Shoot at a unit/building with velocity prediction. lenum.config = Building configuration, e.g. sorter item. lenum.enabled = Whether the block is enabled. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Illuminator color. laccess.controller = Unit controller. If processor controlled, returns processor.\nIf in a formation, returns leader.\nOtherwise, returns the unit itself. laccess.dead = Whether a unit/building is dead or no longer valid. diff --git a/core/assets/bundles/bundle_nl.properties b/core/assets/bundles/bundle_nl.properties index a9565e7a58..21df1a3d23 100644 --- a/core/assets/bundles/bundle_nl.properties +++ b/core/assets/bundles/bundle_nl.properties @@ -2389,6 +2389,7 @@ lenum.shoot = Shoot at a position. lenum.shootp = Shoot at a unit/building with velocity prediction. lenum.config = Building configuration, e.g. sorter item. lenum.enabled = Whether the block is enabled. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Illuminator color. laccess.controller = Unit controller. If processor controlled, returns processor.\nIf in a formation, returns leader.\nOtherwise, returns the unit itself. laccess.dead = Whether a unit/building is dead or no longer valid. diff --git a/core/assets/bundles/bundle_nl_BE.properties b/core/assets/bundles/bundle_nl_BE.properties index 58cfa95b77..bb32cf85be 100644 --- a/core/assets/bundles/bundle_nl_BE.properties +++ b/core/assets/bundles/bundle_nl_BE.properties @@ -2376,6 +2376,7 @@ lenum.shoot = Shoot at a position. lenum.shootp = Shoot at a unit/building with velocity prediction. lenum.config = Building configuration, e.g. sorter item. lenum.enabled = Whether the block is enabled. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Illuminator color. laccess.controller = Unit controller. If processor controlled, returns processor.\nIf in a formation, returns leader.\nOtherwise, returns the unit itself. laccess.dead = Whether a unit/building is dead or no longer valid. diff --git a/core/assets/bundles/bundle_pl.properties b/core/assets/bundles/bundle_pl.properties index a013f5d4d7..87477c01ab 100644 --- a/core/assets/bundles/bundle_pl.properties +++ b/core/assets/bundles/bundle_pl.properties @@ -2412,6 +2412,7 @@ lenum.shoot = Strzel w określoną pozycje. lenum.shootp = Strzel w jednostkę/budynek z zachowaniem trajektorii. lenum.config = Konfiguracja budynku, np. sortownika. lenum.enabled = Sprawdza czy blok jest włączony. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Kolor iluminatora. laccess.controller = Kontroler jednostki. Jeśli jest kontrolowana przez procesor, zwraca procesor.\nJeśli we formacji, zwraca przywódcę.\nW innym wypadku zwraca samą jednostkę. diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index aa7a4ee556..2aadc7d22f 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -2411,6 +2411,7 @@ lenum.shoot = Atire em uma posição. lenum.shootp = Atire em uma unidade/edifício com previsão de velocidade. lenum.config = Configuração do edifício, por ex. item classificador. lenum.enabled = Se o bloco está ativado. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Cor do iluminador. laccess.controller = Controlador de unidade. Se controlado pelo processador, retorna o processador.\nCaso contrário, retorna a própria unidade. diff --git a/core/assets/bundles/bundle_pt_PT.properties b/core/assets/bundles/bundle_pt_PT.properties index 1eccbc6aba..3dbe0a7975 100644 --- a/core/assets/bundles/bundle_pt_PT.properties +++ b/core/assets/bundles/bundle_pt_PT.properties @@ -2376,6 +2376,7 @@ lenum.shoot = Shoot at a position. lenum.shootp = Shoot at a unit/building with velocity prediction. lenum.config = Building configuration, e.g. sorter item. lenum.enabled = Whether the block is enabled. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Illuminator color. laccess.controller = Unit controller. If processor controlled, returns processor.\nIf in a formation, returns leader.\nOtherwise, returns the unit itself. laccess.dead = Whether a unit/building is dead or no longer valid. diff --git a/core/assets/bundles/bundle_ro.properties b/core/assets/bundles/bundle_ro.properties index f619f285bb..cae469702e 100644 --- a/core/assets/bundles/bundle_ro.properties +++ b/core/assets/bundles/bundle_ro.properties @@ -2395,6 +2395,7 @@ lenum.shoot = Lovește către o locație. lenum.shootp = Lovește către o unitate/clădire. Anticipează viteza țintei și a proiectilului. lenum.config = Configurația clădirii, de ex. materialul selectat pt Sortator. lenum.enabled = Specifică dacă clădirea este pornită. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Culoarea iluminatorului. laccess.controller = Controlorul unității. Dacă e controlată de procesor, returnează procesorul.\nDacă e într-o formație, returnează liderul.\nAltfel, returnează unitatea însăși. diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index 9760beacc7..23fd6df9b6 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -2397,6 +2397,7 @@ lenum.shoot = Стрельба в определённую позицию. lenum.shootp = Стрельба в единицу/постройку с расчётом скорости. lenum.config = Конфигурация постройки, например, предмет сортировки. lenum.enabled = Включён ли блок. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Цвет осветителя. laccess.controller = Командующий единицей. Если единица управляется процессором, возвращает процессор. Если в строю, возвращает командующего.\nВ противном случае возвращает саму единицу. diff --git a/core/assets/bundles/bundle_sr.properties b/core/assets/bundles/bundle_sr.properties index 6bb7bb4f38..6e6cf07481 100644 --- a/core/assets/bundles/bundle_sr.properties +++ b/core/assets/bundles/bundle_sr.properties @@ -2398,6 +2398,7 @@ lenum.shoot = Ispaljuj na mestu. lenum.shootp = Shoot at a unit/building with velocity prediction. lenum.config = Konfiguracija građevine, npr. sortirani materijal. lenum.enabled = Da li je ova građevina osposobljena. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Boja iluminatora. laccess.controller = Upravljač jedinice. Ako upravljano putem procesora, šalji "processor".\nAko je upravljano u formaciji, šalji "lider".U ostalim slučajevima, šalji jedinicu za sebe. diff --git a/core/assets/bundles/bundle_sv.properties b/core/assets/bundles/bundle_sv.properties index ae76e627e3..a9cb83d9fb 100644 --- a/core/assets/bundles/bundle_sv.properties +++ b/core/assets/bundles/bundle_sv.properties @@ -2376,6 +2376,7 @@ lenum.shoot = Shoot at a position. lenum.shootp = Shoot at a unit/building with velocity prediction. lenum.config = Building configuration, e.g. sorter item. lenum.enabled = Whether the block is enabled. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Illuminator color. laccess.controller = Unit controller. If processor controlled, returns processor.\nIf in a formation, returns leader.\nOtherwise, returns the unit itself. laccess.dead = Whether a unit/building is dead or no longer valid. diff --git a/core/assets/bundles/bundle_th.properties b/core/assets/bundles/bundle_th.properties index 9b50196aa5..1fe1ed195a 100644 --- a/core/assets/bundles/bundle_th.properties +++ b/core/assets/bundles/bundle_th.properties @@ -2415,6 +2415,7 @@ lenum.shoot = ยิงไปที่ตำแหน่งเป้าหมา lenum.shootp = ยิงเป้าหมายโดยมีการคำนวณการยิง lenum.config = การกำหนดค่าของสิ่งก่อสร้าง เช่น ไอเท็มของเครื่องคัดแยก lenum.enabled = ว่าบล็อกเปิดใช้งาน/ทำงานอยู่หรือเปล่า +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = สีของตัวเปล่งแสง laccess.controller = ผู้ควบคุมยูนิต ถ้าผู้ควบคุมคือตัวประมวลผล จะส่งกลับค่า processor\nนอกนั้น จะส่งกลับค่าตัวยูนิตเอง diff --git a/core/assets/bundles/bundle_tk.properties b/core/assets/bundles/bundle_tk.properties index c468c30579..e625322820 100644 --- a/core/assets/bundles/bundle_tk.properties +++ b/core/assets/bundles/bundle_tk.properties @@ -2376,6 +2376,7 @@ lenum.shoot = Shoot at a position. lenum.shootp = Shoot at a unit/building with velocity prediction. lenum.config = Building configuration, e.g. sorter item. lenum.enabled = Whether the block is enabled. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Illuminator color. laccess.controller = Unit controller. If processor controlled, returns processor.\nIf in a formation, returns leader.\nOtherwise, returns the unit itself. laccess.dead = Whether a unit/building is dead or no longer valid. diff --git a/core/assets/bundles/bundle_tr.properties b/core/assets/bundles/bundle_tr.properties index 1c1c95f1f8..db687017f7 100644 --- a/core/assets/bundles/bundle_tr.properties +++ b/core/assets/bundles/bundle_tr.properties @@ -2395,6 +2395,7 @@ lenum.shoot = Bir konuma ateş et. lenum.shootp = Belli bir birim veya binaya ateş et. lenum.config = Bina yapılandırması, örnek: Ayıklayıcı Türü lenum.enabled = Blok aktif mi? +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Aydınlatıcı Rengi laccess.controller = Birim Kontrol edici. Eğer işlemci kontrol ediyorsa işlemci döner. \nFormasyon durumundaysa, lider döner.\nDiğer şekilde, birimi kendi döner. diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 0d13699812..b50afe1626 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -2422,6 +2422,7 @@ lenum.shoot = Стріляти в зазначену позицію. lenum.shootp = Стріляти в одиницю чи будівлю із передбаченням швидкості. lenum.config = Конфігурація будівлі, як-от в сортувальника. lenum.enabled = Чи блок увімкнено. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Колір освітлювача. laccess.controller = Керувач одиницями. Якщо процесор керує одиницею, повертає процесор.\nЯкщо у формуванні, повертається лідер.\nІнакше повертає саму одиницю. diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index cd61f3d6cb..ca0458b04e 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -2437,6 +2437,7 @@ lenum.shoot = Bắn vào một vị trí. lenum.shootp = Bắn vào một đơn vị/công trình với tốc độ dự đoán. lenum.config = Cấu hình công trình, kiểu như vật phẩm của Khối sắp xếp. lenum.enabled = Khối có đang hoạt động. +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = Màu đèn chiếu sáng. laccess.controller = Thứ điều khiển đơn vị. Nếu khối xử lý đã điều khiển, trả về khối xử lý.\nNgược lại, trả về chính đơn vị đó. diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index f5e11ba156..60a25dd6d4 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -2423,6 +2423,7 @@ lenum.shoot = 向某个位置瞄准/射击 lenum.shootp = 根据提前量向某个单位或建筑瞄准/射击 lenum.config = 建筑设置,例如分类器所设置的筛选物品种类 lenum.enabled = 建筑是否已启用 +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = 照明器发光的颜色 laccess.controller = 单位的控制方\n如果单位由处理器控制,返回对应的处理器\n如果单位在编队中,返回编队的领队\n其他情况,返回单位自身 diff --git a/core/assets/bundles/bundle_zh_TW.properties b/core/assets/bundles/bundle_zh_TW.properties index 26f5bf7575..7ef0eea54f 100644 --- a/core/assets/bundles/bundle_zh_TW.properties +++ b/core/assets/bundles/bundle_zh_TW.properties @@ -2407,6 +2407,7 @@ lenum.shoot = 對該位置開火 lenum.shootp = 對指定單位/建築開火,具自瞄功能 lenum.config = 建築設置,例如分類器篩选的物品種類 lenum.enabled = 確認該建築是否啟用 +laccess.currentammotype = Current ammo item/liquid of a turret. laccess.color = 照明燈顏色 laccess.controller = 單位的控制者。受處理器控制時回傳處理器。\n在隊形中回傳領導的單位。\n否則回傳單位自己。 From 4a456645d328d816f7bc4bc68c3331e8a86a9eea Mon Sep 17 00:00:00 2001 From: MEEPofFaith <54301439+MEEPofFaith@users.noreply.github.com> Date: Sun, 7 Jul 2024 08:38:21 -0700 Subject: [PATCH 301/348] ConsumeItemList (#9947) * ConsumeItemList * Add to ContentParser * Add efficiency multipliers * Untested adding to ObjectIntMap and ObjectFloatMap * Use a single map --- core/src/mindustry/mod/ContentParser.java | 15 +++++++-- .../world/consumers/ConsumeItemList.java | 33 +++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 core/src/mindustry/world/consumers/ConsumeItemList.java diff --git a/core/src/mindustry/mod/ContentParser.java b/core/src/mindustry/mod/ContentParser.java index 8e9f759ffb..05198c401e 100644 --- a/core/src/mindustry/mod/ContentParser.java +++ b/core/src/mindustry/mod/ContentParser.java @@ -467,6 +467,7 @@ public class ContentParser{ case "itemFlammable" -> block.consume((Consume)parser.readValue(ConsumeItemFlammable.class, child)); case "itemRadioactive" -> block.consume((Consume)parser.readValue(ConsumeItemRadioactive.class, child)); case "itemExplosive" -> block.consume((Consume)parser.readValue(ConsumeItemExplosive.class, child)); + case "itemList" -> block.consume((Consume)parser.readValue(ConsumeItemList.class, child)); case "itemExplode" -> block.consume((Consume)parser.readValue(ConsumeItemExplode.class, child)); case "items" -> block.consume(child.isArray() ? new ConsumeItems(parser.readValue(ItemStack[].class, child)) : @@ -1061,17 +1062,25 @@ public class ContentParser{ } Field field = metadata.field; try{ - boolean mergeMap = ObjectMap.class.isAssignableFrom(field.getType()) && child.has("add") && child.get("add").isBoolean() && child.getBoolean("add", false); + boolean isMap = ObjectMap.class.isAssignableFrom(field.getType()) || ObjectIntMap.class.isAssignableFrom(field.getType()) || ObjectFloatMap.class.isAssignableFrom(field.getType()); + boolean mergeMap = isMap && child.has("add") && child.get("add").isBoolean() && child.getBoolean("add", false); if(mergeMap){ child.remove("add"); } Object readField = parser.readValue(field.getType(), metadata.elementType, child, metadata.keyType); + Object fieldObj = field.get(object); //if a map has add: true, add its contents to the map instead - if(mergeMap && field.get(object) instanceof ObjectMap baseMap){ - baseMap.putAll((ObjectMap)readField); + if(mergeMap && (fieldObj instanceof ObjectMap || fieldObj instanceof ObjectIntMap || fieldObj instanceof ObjectFloatMap)){ + if(field.get(object) instanceof ObjectMap baseMap){ + baseMap.putAll((ObjectMap)readField); + }else if(field.get(object) instanceof ObjectIntMap baseMap){ + baseMap.putAll((ObjectIntMap)readField); + }else if(field.get(object) instanceof ObjectFloatMap baseMap){ + baseMap.putAll((ObjectFloatMap)readField); + } }else{ field.set(object, readField); } diff --git a/core/src/mindustry/world/consumers/ConsumeItemList.java b/core/src/mindustry/world/consumers/ConsumeItemList.java new file mode 100644 index 0000000000..7dea4e7a3e --- /dev/null +++ b/core/src/mindustry/world/consumers/ConsumeItemList.java @@ -0,0 +1,33 @@ +package mindustry.world.consumers; + +import arc.struct.*; +import mindustry.gen.*; +import mindustry.type.*; + +public class ConsumeItemList extends ConsumeItemFilter{ + public ObjectFloatMap itemMultipliers = new ObjectFloatMap<>(); + + public ConsumeItemList(Item... items){ + this(); + for(Item i : items){ + itemMultipliers.put(i, 1f); + } + } + + public ConsumeItemList(){ + filter = item -> itemMultipliers.containsKey(item); + } + + /** Initializes item efficiency multiplier map. Format: [item1, mult1, item2, mult2...] */ + public void setMultipliers(Object... objects){ + for(int i = 0; i < objects.length; i += 2){ + itemMultipliers.put((Item)objects[i], (Float)objects[i + 1]); + } + } + + @Override + public float efficiencyMultiplier(Building build){ + var item = getConsumed(build); + return itemMultipliers.get(item, 1f); + } +} From c193206ccb37b2829ff9e42e1f2500fe2d33936c Mon Sep 17 00:00:00 2001 From: MEEPofFaith <54301439+MEEPofFaith@users.noreply.github.com> Date: Sun, 7 Jul 2024 08:42:45 -0700 Subject: [PATCH 302/348] Modded payload factory sprites (#8595) * Search for modded payload factory blocks * Generate payload mass driver outlines in mods * Revert weird change * Revert "Revert weird change" This reverts commit 0afd64077a44094082362f929c820c88beebcfa9. --- .../world/blocks/payloads/PayloadBlock.java | 15 ++++++++++++--- .../world/blocks/payloads/PayloadMassDriver.java | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/core/src/mindustry/world/blocks/payloads/PayloadBlock.java b/core/src/mindustry/world/blocks/payloads/PayloadBlock.java index b4b0de0f37..ab7dc95320 100644 --- a/core/src/mindustry/world/blocks/payloads/PayloadBlock.java +++ b/core/src/mindustry/world/blocks/payloads/PayloadBlock.java @@ -33,9 +33,18 @@ public class PayloadBlock extends Block{ public void load(){ super.load(); - topRegion = Core.atlas.find(name + "-top", "factory-top-" + size + regionSuffix); - outRegion = Core.atlas.find(name + "-out", "factory-out-" + size + regionSuffix); - inRegion = Core.atlas.find(name + "-in", "factory-in-" + size + regionSuffix); + topRegion = findFactoryRegion("-top"); + outRegion = findFactoryRegion("-out"); + inRegion = findFactoryRegion("-in"); + } + + protected TextureRegion findFactoryRegion(String suf){ + TextureRegion region = Core.atlas.find(name + suf); + + if(!region.found() && minfo.mod != null) region = Core.atlas.find(minfo.mod.name + "-factory" + suf + "-" + size + regionSuffix); + if(!region.found()) region = Core.atlas.find("factory" + suf + "-" + size + regionSuffix); + + return region; } public static boolean blends(Building build, int direction){ diff --git a/core/src/mindustry/world/blocks/payloads/PayloadMassDriver.java b/core/src/mindustry/world/blocks/payloads/PayloadMassDriver.java index 0b3efc09c9..0e129a7b39 100644 --- a/core/src/mindustry/world/blocks/payloads/PayloadMassDriver.java +++ b/core/src/mindustry/world/blocks/payloads/PayloadMassDriver.java @@ -123,8 +123,8 @@ public class PayloadMassDriver extends PayloadBlock{ } @Override - public TextureRegion[] makeIconRegions(){ - return new TextureRegion[]{leftRegion, rightRegion, capRegion}; + public void getRegionsToOutline(Seq out){ + out.add(leftRegion, rightRegion, capRegion); } public class PayloadDriverBuild extends PayloadBlockBuild implements RotBlock{ From a184abb117a613ca7963c78035214ac09898f5da Mon Sep 17 00:00:00 2001 From: SomeonesShade <80511312+SomeonesShade@users.noreply.github.com> Date: Sun, 7 Jul 2024 23:52:58 +0800 Subject: [PATCH 303/348] Flux Power and Heat Capacity Increase (#9500) --- core/src/mindustry/content/Blocks.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 969090e462..5524503b49 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -2556,8 +2556,8 @@ public class Blocks{ fluxReactor = new VariableReactor("flux-reactor"){{ requirements(Category.power, with(Items.graphite, 300, Items.carbide, 200, Items.oxide, 100, Items.silicon, 600, Items.surgeAlloy, 300)); - powerProduction = 120f; - maxHeat = 140f; + powerProduction = 240f; + maxHeat = 150f; consumeLiquid(Liquids.cyanogen, 9f / 60f); liquidCapacity = 30f; From cd298edfe70707ab4567dbb081296e7174c315a4 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 7 Jul 2024 12:22:12 -0400 Subject: [PATCH 304/348] Unused variable removed --- core/src/mindustry/ai/BaseBuilderAI.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/core/src/mindustry/ai/BaseBuilderAI.java b/core/src/mindustry/ai/BaseBuilderAI.java index 763fd15ee5..3df72adb70 100644 --- a/core/src/mindustry/ai/BaseBuilderAI.java +++ b/core/src/mindustry/ai/BaseBuilderAI.java @@ -32,7 +32,6 @@ public class BaseBuilderAI{ private static int correct = 0, incorrect = 0; - private int lastX, lastY, lastW, lastH; private boolean foundPath; final TeamData data; @@ -262,11 +261,6 @@ public class BaseBuilderAI{ data.plans.add(new BlockPlan(cx + tile.x, cy + tile.y, tile.rotation, tile.block.id, tile.config)); } - lastX = cx - 1; - lastY = cy - 1; - lastW = result.width + 2; - lastH = result.height + 2; - return true; } } \ No newline at end of file From e3f9e19f1365c759d930d140f652ab52290864d1 Mon Sep 17 00:00:00 2001 From: SITUVNgcd <44901211+SITUVNgcd@users.noreply.github.com> Date: Sun, 7 Jul 2024 23:58:51 +0700 Subject: [PATCH 305/348] Update Vietnamese translation (#10008) https://github.com/Anuken/Mindustry/commit/41aea39a80ff8d9cf56e9af132e2eed5b435323c --- core/assets/bundles/bundle_vi.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index ca0458b04e..c9622ac982 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -234,7 +234,7 @@ host.info = Nút [accent]Mở máy chủ[] mở máy chủ trên cổng [scarlet join.info = Tại đây, bạn có thể nhập [accent]IP máy chủ[] kết nối, hoặc khám phá [accent]mạng cục bộ[] hay kết nối đến máy chủ [accent]toàn cầu[].\nCả mạng LAN và WAN đều được hỗ trợ.\n\n[lightgray]Nếu bạn muốn kết nối với ai đó bằng IP, bạn sẽ cần phải hỏi IP của họ, có thể được tìm thấy bằng cách tra google với từ khóa "my ip" trên thiết bị của họ. hostserver = Mở máy chủ nhiều người chơi invitefriends = Mời bạn bè -hostserver.mobile = Mở máy chủ +hostserver.mobile = Mở máy chủ trò chơi host = Mở máy chủ hosting = [accent]Đang mở máy chủ... hosts.refresh = Làm mới @@ -2437,8 +2437,8 @@ lenum.shoot = Bắn vào một vị trí. lenum.shootp = Bắn vào một đơn vị/công trình với tốc độ dự đoán. lenum.config = Cấu hình công trình, kiểu như vật phẩm của Khối sắp xếp. lenum.enabled = Khối có đang hoạt động. -laccess.currentammotype = Current ammo item/liquid of a turret. +laccess.currentammotype = Đạn vật phẩm/chất lỏng hiện tại của bệ súng. laccess.color = Màu đèn chiếu sáng. laccess.controller = Thứ điều khiển đơn vị. Nếu khối xử lý đã điều khiển, trả về khối xử lý.\nNgược lại, trả về chính đơn vị đó. laccess.dead = Đơn vị/công trình đã chết hoặc không còn hợp lệ hay không. From 76456d6f0eb00a2aecdc960b1520968253e34eea Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 7 Jul 2024 20:14:08 -0400 Subject: [PATCH 306/348] Fixed #10011 --- core/assets/maps/frozenForest.msav | Bin 10604 -> 10745 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/core/assets/maps/frozenForest.msav b/core/assets/maps/frozenForest.msav index e29d7199cd55249d643bdb98e78c65733e43d912..57b06ee25d02b9744874775c72090d14dea2a7b8 100644 GIT binary patch literal 10745 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 NpuE5PWtF_10{~^)Cfoo3 literal 10604 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 b+pqa8enY9tbnT9$i}TNI`!BxC<6 Date: Sun, 7 Jul 2024 20:17:14 -0400 Subject: [PATCH 307/348] Fixed #10010 --- core/src/mindustry/core/Logic.java | 32 ++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/core/src/mindustry/core/Logic.java b/core/src/mindustry/core/Logic.java index 6af657678f..7d1a7bb5b9 100644 --- a/core/src/mindustry/core/Logic.java +++ b/core/src/mindustry/core/Logic.java @@ -47,17 +47,7 @@ public class Logic implements ApplicationListener{ Events.on(BlockBuildEndEvent.class, event -> { if(!event.breaking){ - TeamData data = event.team.data(); - Iterator it = data.plans.iterator(); - var bounds = event.tile.block().bounds(event.tile.x, event.tile.y, Tmp.r1); - while(it.hasNext()){ - BlockPlan b = it.next(); - Block block = content.block(b.block); - if(bounds.overlaps(block.bounds(b.x, b.y, Tmp.r2))){ - b.removed = true; - it.remove(); - } - } + checkOverlappingPlans(event.team, event.tile); if(event.team == state.rules.defaultTeam){ state.stats.placedBlockCount.increment(event.tile.block()); @@ -65,6 +55,12 @@ public class Logic implements ApplicationListener{ } }); + Events.on(PayloadDropEvent.class, e -> { + if(e.build != null){ + checkOverlappingPlans(e.build.team, e.build.tile); + } + }); + //when loading a 'damaged' sector, propagate the damage Events.on(SaveLoadEvent.class, e -> { if(state.isCampaign()){ @@ -211,6 +207,20 @@ public class Logic implements ApplicationListener{ }); } + private void checkOverlappingPlans(Team team, Tile tile){ + TeamData data = team.data(); + Iterator it = data.plans.iterator(); + var bounds = tile.block().bounds(tile.x, tile.y, Tmp.r1); + while(it.hasNext()){ + BlockPlan b = it.next(); + Block block = content.block(b.block); + if(bounds.overlaps(block.bounds(b.x, b.y, Tmp.r2))){ + b.removed = true; + it.remove(); + } + } + } + /** Adds starting items, resets wave time, and sets state to playing. */ public void play(){ state.set(State.playing); From 534f8a3ce2c54652f6d070ff66d01c89287e3824 Mon Sep 17 00:00:00 2001 From: 3MIDEV <112279985+3midev@users.noreply.github.com> Date: Mon, 8 Jul 2024 01:18:53 +0100 Subject: [PATCH 308/348] Update servers_v7.json (#10009) moved off the proxied domain ping was hell --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index fd39dc35a9..403f1bea24 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -242,7 +242,7 @@ }, { "name": "Exdustry", - "address": ["uk1.noxia.cloud:6001", "uk1.noxia.cloud:6002", "uk1.noxia.cloud:6003", "uk1.noxia.cloud:6004", "uk1.noxia.cloud:6005", "uk1.noxia.cloud:6006", "uk1.noxia.cloud:6007", "uk1.noxia.cloud:6008", "uk1.noxia.cloud:6009", "uk1.noxia.cloud:6010"] + "address": ["exd.noxia.cloud:6001", "exd.noxia.cloud:6002", "exd.noxia.cloud:6003", "exd.noxia.cloud:6004", "exd.noxia.cloud:6005", "exd.noxia.cloud:6006", "exd.noxia.cloud:6007", "exd.noxia.cloud:6008", "exd.noxia.cloud:6009", "exd.noxia.cloud:6010"] }, { "name": "abcxyz remaster", From abfbb46bc1513853332fde32855eb1f5fe4306a0 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 8 Jul 2024 11:08:01 -0400 Subject: [PATCH 309/348] Downgrade Android gradle plugin for IJ compatibility --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index e73cace96d..f1dc45aa74 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -7,7 +7,7 @@ buildscript{ } dependencies{ - classpath 'com.android.tools.build:gradle:8.5.0' + classpath 'com.android.tools.build:gradle:8.2.2' } } From 92e121c05c4ad385c38b567c4f6e2e97b8acdc78 Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 10 Jul 2024 13:24:03 -0400 Subject: [PATCH 310/348] Fixed unit part progress with multiple weapons --- core/src/mindustry/type/UnitType.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index 34c3b537df..3408948807 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -1282,9 +1282,9 @@ public class UnitType extends UnlockableContent implements Senseable{ for(int i = 0; i < parts.size; i++){ var part = parts.get(i); - WeaponMount first = unit.mounts.length > part.weaponIndex ? unit.mounts[part.weaponIndex] : null; - if(first != null){ - DrawPart.params.set(first.warmup, first.reload / weapons.first().reload, first.smoothReload, first.heat, first.recoil, first.charge, unit.x, unit.y, unit.rotation); + WeaponMount mount = unit.mounts.length > part.weaponIndex ? unit.mounts[part.weaponIndex] : null; + if(mount != null){ + DrawPart.params.set(mount.warmup, mount.reload / mount.weapon.reload, mount.smoothReload, mount.heat, mount.recoil, mount.charge, unit.x, unit.y, unit.rotation); }else{ DrawPart.params.set(0f, 0f, 0f, 0f, 0f, 0f, unit.x, unit.y, unit.rotation); } From e3ba8b714b6cc38c5d4db10a74bdb3d017cd85c3 Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 10 Jul 2024 15:28:43 -0400 Subject: [PATCH 311/348] Crash fix --- core/src/mindustry/input/InputHandler.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index a23af33bb1..df84886699 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -489,6 +489,8 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ @Remote(targets = Loc.server, called = Loc.server) public static void pickedUnitPayload(Unit unit, Unit target){ + if(target == Nulls.unit) return; + if(target != null && unit instanceof Payloadc pay){ pay.pickup(target); }else if(target != null){ From 295573142ffe28bb5e85a16cc902792d0a808f5e Mon Sep 17 00:00:00 2001 From: Anuken Date: Wed, 10 Jul 2024 15:54:46 -0400 Subject: [PATCH 312/348] Removing Nulls.unit --- .../annotations/entity/EntityProcess.java | 83 ------------------- core/src/mindustry/core/NetClient.java | 19 +++-- core/src/mindustry/core/NetServer.java | 49 ++++++----- .../mindustry/entities/comp/EntityComp.java | 4 - .../mindustry/entities/comp/PlayerComp.java | 48 ++++++----- core/src/mindustry/input/InputHandler.java | 35 ++++---- core/src/mindustry/input/MobileInput.java | 31 ++++--- core/src/mindustry/io/TypeIO.java | 11 ++- core/src/mindustry/logic/LExecutor.java | 2 +- .../ui/fragments/BlockInventoryFragment.java | 2 +- .../mindustry/ui/fragments/HintsFragment.java | 6 +- .../mindustry/ui/fragments/HudFragment.java | 4 +- 12 files changed, 106 insertions(+), 188 deletions(-) diff --git a/annotations/src/main/java/mindustry/annotations/entity/EntityProcess.java b/annotations/src/main/java/mindustry/annotations/entity/EntityProcess.java index 3fa62d8446..3abd583158 100644 --- a/annotations/src/main/java/mindustry/annotations/entity/EntityProcess.java +++ b/annotations/src/main/java/mindustry/annotations/entity/EntityProcess.java @@ -851,89 +851,6 @@ public class EntityProcess extends BaseProcessor{ for(TypeSpec.Builder b : baseClasses){ write(b, imports.toSeq()); } - - //TODO nulls were an awful idea - //store nulls - TypeSpec.Builder nullsBuilder = TypeSpec.classBuilder("Nulls").addModifiers(Modifier.PUBLIC).addModifiers(Modifier.FINAL); - //TODO should be dynamic - ObjectSet nullList = ObjectSet.with("unit"); - - //create mock types of all components - for(Stype interf : allInterfaces){ - //indirect interfaces to implement methods for - Seq dependencies = interf.allInterfaces().add(interf); - Seq methods = dependencies.flatMap(Stype::methods); - methods.sortComparing(Object::toString); - - //optionally add superclass - Stype superclass = dependencies.map(this::interfaceToComp).find(s -> s != null && s.annotation(Component.class).base()); - //use the base type when the interface being emulated has a base - TypeName type = superclass != null && interfaceToComp(interf).annotation(Component.class).base() ? tname(baseName(superclass)) : interf.tname(); - - //used method signatures - ObjectSet signatures = new ObjectSet<>(); - - //create null builder - String baseName = interf.name().substring(0, interf.name().length() - 1); - - //prevent Nulls bloat - if(!nullList.contains(Strings.camelize(baseName))){ - continue; - } - - String className = "Null" + baseName; - TypeSpec.Builder nullBuilder = TypeSpec.classBuilder(className) - .addModifiers(Modifier.FINAL); - - skipDeprecated(nullBuilder); - - nullBuilder.addSuperinterface(interf.tname()); - if(superclass != null) nullBuilder.superclass(tname(baseName(superclass))); - - for(Smethod method : methods){ - String signature = method.toString(); - if(!signatures.add(signature)) continue; - - Stype compType = interfaceToComp(method.type()); - MethodSpec.Builder builder = MethodSpec.overriding(method.e).addModifiers(Modifier.PUBLIC, Modifier.FINAL); - int index = 0; - for(ParameterSpec spec : builder.parameters){ - Reflect.set(spec, "name", "arg" + index++); - } - builder.addAnnotation(OverrideCallSuper.class); //just in case - - if(!method.isVoid()){ - String methodName = method.name(); - switch(methodName){ - case "isNull": - builder.addStatement("return true"); - break; - case "id": - builder.addStatement("return -1"); - break; - case "toString": - builder.addStatement("return $S", className); - break; - default: - Svar variable = compType == null || method.params().size > 0 ? null : compType.fields().find(v -> v.name().equals(methodName)); - String desc = variable == null ? null : variable.descString(); - if(variable == null || !varInitializers.containsKey(desc)){ - builder.addStatement("return " + getDefault(method.ret().toString())); - }else{ - String init = varInitializers.get(desc); - builder.addStatement("return " + (init.equals("{}") ? "new " + variable.mirror().toString() : "") + init); - } - } - } - nullBuilder.addMethod(builder.build()); - } - - nullsBuilder.addField(FieldSpec.builder(type, Strings.camelize(baseName)).initializer("new " + className + "()").addModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PUBLIC).build()); - - write(nullBuilder, imports.toSeq()); - } - - write(nullsBuilder); } } diff --git a/core/src/mindustry/core/NetClient.java b/core/src/mindustry/core/NetClient.java index 8171a89797..ec6dc2b0fc 100644 --- a/core/src/mindustry/core/NetClient.java +++ b/core/src/mindustry/core/NetClient.java @@ -622,21 +622,22 @@ public class NetClient implements ApplicationListener{ void sync(){ if(timer.get(0, playerSyncTime)){ - Unit unit = player.dead() ? Nulls.unit : player.unit(); - int uid = player.dead() ? -1 : unit.id; + boolean dead = player.dead(); + Unit unit = dead ? null : player.unit(); + int uid = dead || unit == null ? -1 : unit.id; Call.clientSnapshot( lastSent++, uid, - player.dead(), - player.dead() ? player.x : unit.x, player.dead() ? player.y : unit.y, - player.unit().aimX(), player.unit().aimY(), - unit.rotation, + dead, + dead ? player.x : unit.x, dead ? player.y : unit.y, + dead ? 0f : unit.aimX(), dead ? 0f : unit.aimY(), + unit == null ? 0f : unit.rotation, unit instanceof Mechc m ? m.baseRotation() : 0, - unit.vel.x, unit.vel.y, - player.unit().mineTile, + unit == null ? 0f : unit.vel.x, unit == null ? 0f : unit.vel.y, + dead ? null : unit.mineTile, player.boosting, player.shooting, ui.chatfrag.shown(), control.input.isBuilding, - player.isBuilder() ? player.unit().plans : null, + player.isBuilder() && unit != null ? unit.plans : null, Core.camera.position.x, Core.camera.position.y, Core.camera.width, Core.camera.height ); diff --git a/core/src/mindustry/core/NetServer.java b/core/src/mindustry/core/NetServer.java index 6d26174479..e6a7c75de1 100644 --- a/core/src/mindustry/core/NetServer.java +++ b/core/src/mindustry/core/NetServer.java @@ -98,7 +98,7 @@ public class NetServer implements ApplicationListener{ private boolean closing = false, pvpAutoPaused = true; private Interval timer = new Interval(10); private IntSet buildHealthChanged = new IntSet(); - + /** Current kick session. */ public @Nullable VoteSession currentlyKicking = null; /** Duration of a kick in seconds. */ @@ -548,10 +548,10 @@ public class NetServer implements ApplicationListener{ @Remote(targets = Loc.client, variants = Variant.one) public static void requestDebugStatus(Player player){ int flags = - (player.con.hasDisconnected ? 1 : 0) | - (player.con.hasConnected ? 2 : 0) | - (player.isAdded() ? 4 : 0) | - (player.con.hasBegunConnecting ? 8 : 0); + (player.con.hasDisconnected ? 1 : 0) | + (player.con.hasConnected ? 2 : 0) | + (player.isAdded() ? 4 : 0) | + (player.con.hasBegunConnecting ? 8 : 0); Call.debugStatusClient(player.con, flags, player.con.lastReceivedClientSnapshot, player.con.snapshotsSent); Call.debugStatusClientUnreliable(player.con, flags, player.con.lastReceivedClientSnapshot, player.con.snapshotsSent); @@ -610,18 +610,18 @@ public class NetServer implements ApplicationListener{ @Remote(targets = Loc.client, unreliable = true) public static void clientSnapshot( - Player player, - int snapshotID, - int unitID, - boolean dead, - float x, float y, - float pointerX, float pointerY, - float rotation, float baseRotation, - float xVelocity, float yVelocity, - Tile mining, - boolean boosting, boolean shooting, boolean chatting, boolean building, - @Nullable Queue plans, - float viewX, float viewY, float viewWidth, float viewHeight + Player player, + int snapshotID, + int unitID, + boolean dead, + float x, float y, + float pointerX, float pointerY, + float rotation, float baseRotation, + float xVelocity, float yVelocity, + Tile mining, + boolean boosting, boolean shooting, boolean chatting, boolean building, + @Nullable Queue plans, + float viewX, float viewY, float viewWidth, float viewHeight ){ NetConnection con = player.con; if(con == null || snapshotID < con.lastReceivedClientSnapshot) return; @@ -660,12 +660,11 @@ public class NetServer implements ApplicationListener{ player.shooting = shooting; player.boosting = boosting; - player.unit().controlWeapons(shooting, shooting); - player.unit().aim(pointerX, pointerY); + @Nullable var unit = player.unit(); if(player.isBuilder()){ - player.unit().clearBuilding(); - player.unit().updateBuilding(building); + unit.clearBuilding(); + unit.updateBuilding(building); if(plans != null){ for(BuildPlan req : plans){ @@ -694,12 +693,12 @@ public class NetServer implements ApplicationListener{ } } - player.unit().mineTile = mining; - con.rejectedRequests.clear(); if(!player.dead()){ - Unit unit = player.unit(); + unit.controlWeapons(shooting, shooting); + unit.aim(pointerX, pointerY); + unit.mineTile = mining; long elapsed = Math.min(Time.timeSinceMillis(con.lastReceivedClientTime), 1500); float maxSpeed = unit.speed(); @@ -1125,7 +1124,7 @@ public class NetServer implements ApplicationListener{ voted.put(admins.getInfo(player.uuid()).lastIP, d); Call.sendMessage(Strings.format("[lightgray]@[lightgray] has voted on kicking[orange] @[lightgray].[accent] (@/@)\n[lightgray]Type[orange] /vote [] to agree.", - player.name, target.name, votes, votesRequired())); + player.name, target.name, votes, votesRequired())); checkPass(); } diff --git a/core/src/mindustry/entities/comp/EntityComp.java b/core/src/mindustry/entities/comp/EntityComp.java index fdba35a737..37b7bb7a2b 100644 --- a/core/src/mindustry/entities/comp/EntityComp.java +++ b/core/src/mindustry/entities/comp/EntityComp.java @@ -35,10 +35,6 @@ abstract class EntityComp{ return ((Object)this) instanceof Unitc u && u.isPlayer() && !isLocal(); } - boolean isNull(){ - return false; - } - /** Replaced with `this` after code generation. */ T self(){ return (T)this; diff --git a/core/src/mindustry/entities/comp/PlayerComp.java b/core/src/mindustry/entities/comp/PlayerComp.java index f970cfe23b..0ed2a11ad4 100644 --- a/core/src/mindustry/entities/comp/PlayerComp.java +++ b/core/src/mindustry/entities/comp/PlayerComp.java @@ -33,7 +33,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra @Import float x, y; - @ReadOnly Unit unit = Nulls.unit; + @ReadOnly @Nullable Unit unit; transient @Nullable NetConnection con; @ReadOnly Team team = Team.sharded; @SyncLocal boolean typing, shooting, boosting; @@ -49,12 +49,12 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra transient float textFadeTime; transient Ratekeeper itemDepositRate = new Ratekeeper(); - transient private Unit lastReadUnit = Nulls.unit; + transient private @Nullable Unit lastReadUnit; transient private int wrongReadUnits; transient @Nullable Unit justSwitchFrom, justSwitchTo; public boolean isBuilder(){ - return unit.canBuild(); + return unit != null && unit.canBuild(); } public @Nullable CoreBuild closestCore(){ @@ -89,7 +89,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra x = y = 0f; if(!dead()){ unit.resetController(); - unit = Nulls.unit; + unit = null; } } @@ -105,7 +105,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra @Replace public float clipSize(){ - return unit.isNull() ? 20 : unit.type.hitSize * 2f; + return unit == null ? 20 : unit.type.hitSize * 2f; } @Override @@ -131,17 +131,18 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra unit = lastReadUnit; unit(set); lastReadUnit = unit; - - unit.aim(mouseX, mouseY); - //this is only necessary when the thing being controlled isn't synced - unit.controlWeapons(shooting, shooting); - //extra precaution, necessary for non-synced things - unit.controller(this); + if(unit != null){ + unit.aim(mouseX, mouseY); + //this is only necessary when the thing being controlled isn't synced + unit.controlWeapons(shooting, shooting); + //extra precaution, necessary for non-synced things + unit.controller(this); + } } @Override public void update(){ - if(!unit.isValid()){ + if(unit != null && !unit.isValid()){ clearUnit(); } @@ -181,42 +182,43 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra @Override public void remove(){ //clear unit upon removal - if(!unit.isNull()){ + if(unit != null){ clearUnit(); } - lastReadUnit = Nulls.unit; + lastReadUnit = null; justSwitchTo = justSwitchFrom = null; } public void team(Team team){ this.team = team; - unit.team(team); + if(unit != null){ + unit.team(team); + } } public void clearUnit(){ - unit(Nulls.unit); + unit(null); } - public Unit unit(){ + public @Nullable Unit unit(){ return unit; } - public void unit(Unit unit){ + public void unit(@Nullable Unit unit){ //refuse to switch when the unit was just transitioned from if(isLocal() && unit == justSwitchFrom && justSwitchFrom != null && justSwitchTo != null){ return; } - if(unit == null) throw new IllegalArgumentException("Unit cannot be null. Use clearUnit() instead."); if(this.unit == unit) return; //save last command this unit had - if(unit.controller() instanceof CommandAI ai){ + if(unit != null && unit.controller() instanceof CommandAI ai){ lastCommand = ai.command; } - if(this.unit != Nulls.unit){ + if(this.unit != null){ //un-control the old unit this.unit.resetController(); //restore last command issued before it was controlled @@ -225,7 +227,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra } } this.unit = unit; - if(unit != Nulls.unit){ + if(unit != null){ unit.team(team); unit.controller(this); @@ -244,7 +246,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra } boolean dead(){ - return unit.isNull() || !unit.isValid(); + return unit == null || !unit.isValid(); } String ip(){ diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index df84886699..954a81cdb5 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -116,7 +116,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ private WidgetGroup group = new WidgetGroup(); private final Eachable allPlans = cons -> { - player.unit().plans().each(cons); + if(!player.dead()){ + player.unit().plans().each(cons); + } selectPlans.each(cons); linePlans.each(cons); }; @@ -236,9 +238,6 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ public static void commandUnits(Player player, int[] unitIds, @Nullable Building buildTarget, @Nullable Unit unitTarget, @Nullable Vec2 posTarget, boolean queueCommand, boolean finalBatch){ if(player == null || unitIds == null) return; - //why did I ever think this was a good idea - if(unitTarget != null && unitTarget.isNull()) unitTarget = null; - if(net.server() && !netServer.admins.allowAction(player, ActionType.commandUnits, event -> { event.unitIDs = unitIds; })){ @@ -260,7 +259,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ } if(teamTarget != null && teamTarget.team() != player.team() && - !(teamTarget instanceof Unit u && !unit.canTarget(u)) && !(teamTarget instanceof Building && !unit.type.targetGround)){ + !(teamTarget instanceof Unit u && !unit.canTarget(u)) && !(teamTarget instanceof Building && !unit.type.targetGround)){ anyCommandedTarget = true; if(queueCommand){ @@ -282,7 +281,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ if(ai.commandQueue.size <= 0){ ai.group = null; } - + //remove when other player command if(!headless && player != Vars.player){ control.input.selectedUnits.remove(unit); @@ -489,8 +488,6 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ @Remote(targets = Loc.server, called = Loc.server) public static void pickedUnitPayload(Unit unit, Unit target){ - if(target == Nulls.unit) return; - if(target != null && unit instanceof Payloadc pay){ pay.pickup(target); }else if(target != null){ @@ -599,7 +596,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ if(build == null) return; if(net.server() && (!Units.canInteract(player, build) || - !netServer.admins.allowAction(player, ActionType.rotate, build.tile(), action -> action.rotation = Mathf.mod(build.rotation + Mathf.sign(direction), 4)))){ + !netServer.admins.allowAction(player, ActionType.rotate, build.tile(), action -> action.rotation = Mathf.mod(build.rotation + Mathf.sign(direction), 4)))){ throw new ValidateException(player, "Player cannot rotate a block."); } @@ -618,7 +615,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ if(build == null) return; if(net.server() && (!Units.canInteract(player, build) || - !netServer.admins.allowAction(player, ActionType.configure, build.tile, action -> action.config = value))){ + !netServer.admins.allowAction(player, ActionType.configure, build.tile, action -> action.config = value))){ if(player.con != null){ var packet = new TileConfigCallPacket(); //undo the config on the client @@ -697,7 +694,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ player.unit(unit); - if(before != null && !before.isNull()){ + if(before != null){ if(before.spawnedByCore){ unit.dockedType = before.type; }else if(before.dockedType != null && before.dockedType.coreUnitDock){ @@ -812,7 +809,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ } playerPlanTree.clear(); - player.unit().plans.each(playerPlanTree::insert); + if(!player.dead()){ + player.unit().plans.each(playerPlanTree::insert); + } player.typing = ui.chatfrag.shown(); @@ -824,7 +823,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ player.unit().updateBuilding(isBuilding); } - if(player.shooting && !wasShooting && player.unit().hasWeapons() && state.rules.unitAmmo && !player.team().rules().infiniteAmmo && player.unit().ammo <= 0){ + if(!player.dead() && player.shooting && !wasShooting && player.unit().hasWeapons() && state.rules.unitAmmo && !player.team().rules().infiniteAmmo && player.unit().ammo <= 0){ player.unit().type.weapons.first().noAmmoSound.at(player.unit()); } @@ -1673,9 +1672,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ boolean canMine(Tile tile){ return !Core.scene.hasMouse() - && player.unit().validMine(tile) - && player.unit().acceptsItem(player.unit().getMineResult(tile)) - && !((!Core.settings.getBool("doubletapmine") && tile.floor().playerUnmineable) && tile.overlay().itemDrop == null); + && player.unit().validMine(tile) + && player.unit().acceptsItem(player.unit().getMineResult(tile)) + && !((!Core.settings.getBool("doubletapmine") && tile.floor().playerUnmineable) && tile.overlay().itemDrop == null); } /** Returns the tile at the specified MOUSE coordinates. */ @@ -1841,7 +1840,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ public boolean canShoot(){ return block == null && !onConfigurable() && !isDroppingItem() && !player.unit().activelyBuilding() && - !(player.unit() instanceof Mechc && player.unit().isFlying()) && !player.unit().mining() && !commandMode; + !(player.unit() instanceof Mechc && player.unit().isFlying()) && !player.unit().mining() && !commandMode; } public boolean onConfigurable(){ @@ -1867,7 +1866,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ ItemStack stack = player.unit().stack; if(build != null && build.acceptStack(stack.item, stack.amount, player.unit()) > 0 && build.interactable(player.team()) && - build.block.hasItems && player.unit().stack().amount > 0 && build.interactable(player.team())){ + build.block.hasItems && player.unit().stack().amount > 0 && build.interactable(player.team())){ if(!(state.rules.onlyDepositCore && !(build instanceof CoreBuild)) && itemDepositCooldown <= 0f){ Call.transferInventory(player, build); diff --git a/core/src/mindustry/input/MobileInput.java b/core/src/mindustry/input/MobileInput.java index 2d0fc1bec1..ce16f8d915 100644 --- a/core/src/mindustry/input/MobileInput.java +++ b/core/src/mindustry/input/MobileInput.java @@ -90,7 +90,7 @@ public class MobileInput extends InputHandler implements GestureListener{ void checkTargets(float x, float y){ Unit unit = Units.closestEnemy(player.team(), x, y, 20f, u -> !u.dead); - if(unit != null && player.unit().type.canAttack){ + if(unit != null && !player.dead() && player.unit().type.canAttack){ player.unit().mineTile = null; target = unit; }else{ @@ -126,18 +126,21 @@ public class MobileInput extends InputHandler implements GestureListener{ } } - for(var plan : player.unit().plans()){ - Tile other = world.tile(plan.x, plan.y); + if(!player.dead()){ + for(var plan : player.unit().plans()){ + Tile other = world.tile(plan.x, plan.y); - if(other == null || plan.breaking) continue; + if(other == null || plan.breaking) continue; - r1.setSize(plan.block.size * tilesize); - r1.setCenter(other.worldx() + plan.block.offset, other.worldy() + plan.block.offset); + r1.setSize(plan.block.size * tilesize); + r1.setCenter(other.worldx() + plan.block.offset, other.worldy() + plan.block.offset); - if(r2.overlaps(r1)){ - return true; + if(r2.overlaps(r1)){ + return true; + } } } + return false; } @@ -263,7 +266,7 @@ public class MobileInput extends InputHandler implements GestureListener{ } boolean showCancel(){ - return (player.unit().isBuilding() || block != null || mode == breaking || !selectPlans.isEmpty()) && !hasSchem(); + return !player.dead() && (player.unit().isBuilding() || block != null || mode == breaking || !selectPlans.isEmpty()) && !hasSchem(); } boolean hasSchem(){ @@ -277,7 +280,9 @@ public class MobileInput extends InputHandler implements GestureListener{ t.visible(this::showCancel); t.bottom().left(); t.button("@cancel", Icon.cancel, () -> { - player.unit().clearBuilding(); + if(!player.dead()){ + player.unit().clearBuilding(); + } selectPlans.clear(); mode = none; block = null; @@ -864,7 +869,7 @@ public class MobileInput extends InputHandler implements GestureListener{ } } - if(player.shooting && (player.unit().activelyBuilding() || player.unit().mining())){ + if(player.shooting && !player.dead() && (player.unit().activelyBuilding() || player.unit().mining())){ player.shooting = false; } } @@ -1037,7 +1042,7 @@ public class MobileInput extends InputHandler implements GestureListener{ unit.movePref(movement); //update shooting if not building + not mining - if(!player.unit().activelyBuilding() && player.unit().mineTile == null){ + if(!unit.activelyBuilding() && unit.mineTile == null){ //autofire targeting if(manualShooting){ @@ -1046,7 +1051,7 @@ public class MobileInput extends InputHandler implements GestureListener{ }else if(target == null){ player.shooting = false; if(Core.settings.getBool("autotarget") && !(player.unit() instanceof BlockUnitUnit u && u.tile() instanceof ControlBlock c && !c.shouldAutoTarget())){ - if(player.unit().type.canAttack){ + if(unit.type.canAttack){ target = Units.closestTarget(unit.team, unit.x, unit.y, range, u -> u.checkTarget(type.targetAir, type.targetGround), u -> type.targetGround); } diff --git a/core/src/mindustry/io/TypeIO.java b/core/src/mindustry/io/TypeIO.java index 70d1543c36..c02ceb4ae6 100644 --- a/core/src/mindustry/io/TypeIO.java +++ b/core/src/mindustry/io/TypeIO.java @@ -279,7 +279,7 @@ public class TypeIO{ } public static void writeUnit(Writes write, Unit unit){ - write.b(unit == null || unit.isNull() ? 0 : unit instanceof BlockUnitc ? 1 : 2); + write.b(unit == null ? 0 : unit instanceof BlockUnitc ? 1 : 2); //block units are special if(unit instanceof BlockUnitc){ @@ -295,15 +295,14 @@ public class TypeIO{ byte type = read.b(); int id = read.i(); //nothing - if(type == 0) return Nulls.unit; + if(type == 0) return null; if(type == 2){ //standard unit - Unit unit = Groups.unit.getByID(id); - return unit == null ? Nulls.unit : unit; + return Groups.unit.getByID(id); }else if(type == 1){ //block Building tile = world.build(id); - return tile instanceof ControlBlock cont ? cont.unit() : Nulls.unit; + return tile instanceof ControlBlock cont ? cont.unit() : null; } - return Nulls.unit; + return null; } public static void writeCommand(Writes write, @Nullable UnitCommand command){ diff --git a/core/src/mindustry/logic/LExecutor.java b/core/src/mindustry/logic/LExecutor.java index 8ccfd4627f..481895ea4c 100644 --- a/core/src/mindustry/logic/LExecutor.java +++ b/core/src/mindustry/logic/LExecutor.java @@ -1242,7 +1242,7 @@ public class LExecutor{ result.setobj(units == null || i < 0 || i >= units.size ? null : units.get(i)); } } - case player -> result.setobj(i < 0 || i >= data.players.size || data.players.get(i).unit().isNull() ? null : data.players.get(i).unit()); + case player -> result.setobj(i < 0 || i >= data.players.size ? null : data.players.get(i).unit()); case core -> result.setobj(i < 0 || i >= data.cores.size ? null : data.cores.get(i)); case build -> { Block block = extra.obj() instanceof Block b ? b : null; diff --git a/core/src/mindustry/ui/fragments/BlockInventoryFragment.java b/core/src/mindustry/ui/fragments/BlockInventoryFragment.java index 84f5bc221c..54a36cde6e 100644 --- a/core/src/mindustry/ui/fragments/BlockInventoryFragment.java +++ b/core/src/mindustry/ui/fragments/BlockInventoryFragment.java @@ -152,7 +152,7 @@ public class BlockInventoryFragment{ container.add(i); - Boolp canPick = () -> player.unit().acceptsItem(item) && !state.isPaused() && player.within(build, itemTransferRange); + Boolp canPick = () -> !player.dead() && player.unit().acceptsItem(item) && !state.isPaused() && player.within(build, itemTransferRange); HandCursorListener l = new HandCursorListener(); l.enabled = canPick; diff --git a/core/src/mindustry/ui/fragments/HintsFragment.java b/core/src/mindustry/ui/fragments/HintsFragment.java index 75528b9923..77d3335119 100644 --- a/core/src/mindustry/ui/fragments/HintsFragment.java +++ b/core/src/mindustry/ui/fragments/HintsFragment.java @@ -167,7 +167,7 @@ public class HintsFragment{ zoom(visibleDesktop, () -> Core.input.axis(KeyCode.scroll) != 0), breaking(() -> isTutorial.get() && state.rules.defaultTeam.data().getCount(Blocks.conveyor) > 5, () -> ui.hints.events.contains("break")), desktopShoot(visibleDesktop, () -> isSerpulo() && Vars.state.enemies > 0, () -> player.shooting), - depositItems(() -> player.unit().hasItem(), () -> !player.unit().hasItem()), + depositItems(() -> !player.dead() && player.unit().hasItem(), () -> !player.dead() && !player.unit().hasItem()), desktopPause(visibleDesktop, () -> isTutorial.get() && !Vars.net.active() && state.wave >= 2, () -> Core.input.keyTap(Binding.pause)), unitControl(() -> isSerpulo() && state.rules.defaultTeam.data().units.size > 2 && !net.active() && !player.dead(), () -> !player.dead() && !player.unit().spawnedByCore), unitSelectControl(() -> isSerpulo() && state.rules.defaultTeam.data().units.size > 3 && !net.active() && !player.dead(), @@ -179,8 +179,8 @@ public class HintsFragment{ boost(visibleDesktop, () -> !player.dead() && player.unit().type.canBoost, () -> Core.input.keyDown(Binding.boost)), blockInfo(() -> !(state.isCampaign() && state.rules.sector == SectorPresets.groundZero.sector && state.wave < 3), () -> ui.content.isShown()), derelict(() -> ui.hints.events.contains("derelictmouse") && !isTutorial.get(), () -> ui.hints.events.contains("derelictbreak")), - payloadPickup(() -> isSerpulo() && !player.unit().dead && player.unit() instanceof Payloadc p && p.payloads().isEmpty(), () -> player.unit() instanceof Payloadc p && p.payloads().any()), - payloadDrop(() -> !player.unit().dead && player.unit() instanceof Payloadc p && p.payloads().any(), () -> player.unit() instanceof Payloadc p && p.payloads().isEmpty()), + payloadPickup(() -> isSerpulo() && !player.dead() && player.unit() instanceof Payloadc p && p.payloads().isEmpty(), () -> player.unit() instanceof Payloadc p && p.payloads().any()), + payloadDrop(() -> !player.dead() && player.unit() instanceof Payloadc p && p.payloads().any(), () -> player.unit() instanceof Payloadc p && p.payloads().isEmpty()), waveFire(() -> Groups.fire.size() > 0 && Blocks.wave.unlockedNow(), () -> indexer.getFlagged(state.rules.defaultTeam, BlockFlag.extinguisher).size > 0), generator(() -> control.input.block == Blocks.combustionGenerator, () -> ui.hints.placedBlocks.contains(Blocks.combustionGenerator)), rebuildSelect(() -> state.rules.defaultTeam.data().plans.size >= 10, () -> control.input.isRebuildSelecting()), diff --git a/core/src/mindustry/ui/fragments/HudFragment.java b/core/src/mindustry/ui/fragments/HudFragment.java index c882edc714..7e1f78b217 100644 --- a/core/src/mindustry/ui/fragments/HudFragment.java +++ b/core/src/mindustry/ui/fragments/HudFragment.java @@ -767,7 +767,7 @@ public class HudFragment{ } }); - t.add(new SideBar(() -> player.unit().healthf(), () -> true, true)).width(bw).growY().padRight(pad); + t.add(new SideBar(() -> player.dead() ? 0f : player.unit().healthf(), () -> true, true)).width(bw).growY().padRight(pad); t.image(() -> player.icon()).scaling(Scaling.bounded).grow().maxWidth(54f); t.add(new SideBar(() -> player.dead() ? 0f : player.displayAmmo() ? player.unit().ammof() : player.unit().healthf(), () -> !player.displayAmmo(), false)).width(bw).growY().padLeft(pad).update(b -> { b.color.set(player.displayAmmo() ? player.dead() || player.unit() instanceof BlockUnitc ? Pal.ammo : player.unit().type.ammoType.color() : Pal.health); @@ -913,7 +913,7 @@ public class HudFragment{ table.table().update(t -> { t.left(); - Bits applied = player.unit().statusBits(); + Bits applied = player.unit() == null ? null : player.unit().statusBits(); if(!statuses.equals(applied)){ t.clear(); From 2075e22ef174c2263eca01fd37faf05e8cd7d24a Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 12 Jul 2024 13:15:12 -0400 Subject: [PATCH 313/348] Fixed #10020 --- core/src/mindustry/input/DesktopInput.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/input/DesktopInput.java b/core/src/mindustry/input/DesktopInput.java index 7077a1dfbc..86e635c357 100644 --- a/core/src/mindustry/input/DesktopInput.java +++ b/core/src/mindustry/input/DesktopInput.java @@ -57,7 +57,7 @@ public class DesktopInput extends InputHandler{ public long lastCtrlGroupSelectMillis; boolean showHint(){ - return ui.hudfrag.shown && Core.settings.getBool("hints") && selectPlans.isEmpty() && + return ui.hudfrag.shown && Core.settings.getBool("hints") && selectPlans.isEmpty() && !player.dead() && (!isBuilding && !Core.settings.getBool("buildautopause") || player.unit().isBuilding() || !player.dead() && !player.unit().spawnedByCore()); } From ca8a4e1e4fa76c58e55ddf84ea3b3999be0a430e Mon Sep 17 00:00:00 2001 From: summoner001 Date: Fri, 12 Jul 2024 19:19:16 +0200 Subject: [PATCH 314/348] Update bundle_hu.properties (#10013) * Update bundle_hu.properties Translating to hungarian of the new strings. * Update bundle_hu.properties * Update bundle_hu.properties --- core/assets/bundles/bundle_hu.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index 5d4de8ef2c..b4bc215ef8 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -1136,8 +1136,8 @@ category.items = Nyersanyagok category.crafting = Bemenet/kimenet category.function = Funkció category.optional = Lehetséges fejlesztések -setting.alwaysmusic.name = Always Play Music -setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. +setting.alwaysmusic.name = Folyamatos zenelejátszás +setting.alwaysmusic.description = Ha engedélyezve van, akkor a zene folyamatosan szól a játékban.\nHa ki van kapcsolva, akkor csak véletlenszerű időközönként szólal meg. setting.skipcoreanimation.name = Támaszpont indítási/leszállási animáció kihagyása setting.landscape.name = Fekvő mód zárolása setting.shadows.name = Árnyékok @@ -2437,7 +2437,7 @@ lenum.shoot = Lövés egy adott pontra. lenum.shootp = Lövés egy egységre/épületre sebesség-előrejelzéssel. lenum.config = Épületkonfiguráció, például nyersanyag-válogató. lenum.enabled = Engedélyezve van-e a blokk. -laccess.currentammotype = Current ammo item/liquid of a turret. +laccess.currentammotype = Egy lövegtorony jelenlegi lőszer nyersanyaga/folyadéka. laccess.color = Megvilágítás színe. laccess.controller = Egységvezérlő. Ha processzor vezérli, akkor a processzort adja vissza.\nKülönben magát az egységet adja vissza. From d3b952a2102d1b8a2e3a908f45bf1812ea851456 Mon Sep 17 00:00:00 2001 From: StalkerBaran <120944331+StalkerBaran@users.noreply.github.com> Date: Sat, 13 Jul 2024 00:19:36 +0700 Subject: [PATCH 315/348] Update servers_v7.json (#10015) Update MeowIsland --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 403f1bea24..f3d68494d4 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -238,7 +238,7 @@ }, { "name": "MeowIsland", - "address": ["jupiter.minerent.net:25651", "nexus.minerent.net:25596", "nexus.minerent.net:25598", "pandora.minerent.net:25620", "vega.minerent.net:25635"] + "address": ["meowisland.ru:5000", "meowisland.ru:5001", "meowisland.ru:5002", "meowisland.ru:5003", "meowisland.ru:5004"] }, { "name": "Exdustry", From 00e7271f314a1edbdf84de3e8413d1cb477caaa7 Mon Sep 17 00:00:00 2001 From: router Date: Fri, 12 Jul 2024 20:19:44 +0300 Subject: [PATCH 316/348] Update servers_v7.json (#10017) kill me --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index f3d68494d4..b154219520 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -310,7 +310,7 @@ }, { "name": "ArmyOFUkraine", - "address": ["194.247.42.131:27715", "194.247.42.131:26639"] + "address": ["194.247.42.131:27715", "194.247.42.131:27512"] }, { "name": "SandBox", From f90de0af9c81e8cdb8b4e4de3e1d750a7f531a97 Mon Sep 17 00:00:00 2001 From: abcxyzDustry <138785336+abcxyzDustry@users.noreply.github.com> Date: Sat, 13 Jul 2024 00:22:08 +0700 Subject: [PATCH 317/348] Update servers_v7.json (#10021) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index b154219520..70123db09f 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -246,7 +246,7 @@ }, { "name": "abcxyz remaster", - "address": ["144.76.57.59:30302", "srv24.godlike.club:27272"] + "address": ["144.76.57.59:30302", "23.88.73.88:16895"] }, { "name": "CroCraft Network", From 5a493c0338664a70aec2e715fb52d814db12c001 Mon Sep 17 00:00:00 2001 From: Sh1p*nfire <73347888+Sh1penfire@users.noreply.github.com> Date: Sun, 14 Jul 2024 00:30:36 +1000 Subject: [PATCH 318/348] Update CoreBlock.java (#10025) Allows destroyBullet to work on coreBlock. --- core/src/mindustry/world/blocks/storage/CoreBlock.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/mindustry/world/blocks/storage/CoreBlock.java b/core/src/mindustry/world/blocks/storage/CoreBlock.java index e4a2a02672..c3d50f69d7 100644 --- a/core/src/mindustry/world/blocks/storage/CoreBlock.java +++ b/core/src/mindustry/world/blocks/storage/CoreBlock.java @@ -601,6 +601,7 @@ public class CoreBlock extends StorageBlock{ @Override public void afterDestroyed(){ + super.afterDestroyed(); if(state.rules.coreCapture){ if(!net.client()){ tile.setBlock(block, lastDamage); From 2a95f1fe16a3ff87155c0fc56adf4210b89f512e Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 15 Jul 2024 10:24:30 -0400 Subject: [PATCH 319/348] Possible stack conveyor freeze fix --- core/src/mindustry/world/blocks/distribution/StackConveyor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/world/blocks/distribution/StackConveyor.java b/core/src/mindustry/world/blocks/distribution/StackConveyor.java index 334261712e..5e91d18ee6 100644 --- a/core/src/mindustry/world/blocks/distribution/StackConveyor.java +++ b/core/src/mindustry/world/blocks/distribution/StackConveyor.java @@ -280,7 +280,7 @@ public class StackConveyor extends Block implements Autotiler{ if(!enabled) return; if(state == stateUnload){ //unload - while(lastItem != null && (!outputRouter ? moveForward(lastItem) : dump(lastItem))){ + while(lastItem != null && items.has(lastItem) && !outputRouter ? moveForward(lastItem) : dump(lastItem)){ if(!outputRouter){ items.remove(lastItem, 1); } From dac74f3022aaae2d890fb42ac90c41d23190a318 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 15 Jul 2024 10:25:47 -0400 Subject: [PATCH 320/348] Possible stack conveyor freeze fix (2) --- core/src/mindustry/world/blocks/distribution/StackConveyor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/mindustry/world/blocks/distribution/StackConveyor.java b/core/src/mindustry/world/blocks/distribution/StackConveyor.java index 5e91d18ee6..f79496af93 100644 --- a/core/src/mindustry/world/blocks/distribution/StackConveyor.java +++ b/core/src/mindustry/world/blocks/distribution/StackConveyor.java @@ -394,6 +394,7 @@ public class StackConveyor extends Block implements Autotiler{ link = read.i(); cooldown = read.f(); + lastItem = items.first(); } } } From afc8d5e396b234f8a12b3b3a5c739b65b8a5be0e Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 15 Jul 2024 10:45:32 -0400 Subject: [PATCH 321/348] Possible stack conveyor freeze fix (3) --- .../mindustry/world/blocks/distribution/StackConveyor.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/world/blocks/distribution/StackConveyor.java b/core/src/mindustry/world/blocks/distribution/StackConveyor.java index f79496af93..9bf0bcf4cf 100644 --- a/core/src/mindustry/world/blocks/distribution/StackConveyor.java +++ b/core/src/mindustry/world/blocks/distribution/StackConveyor.java @@ -280,14 +280,15 @@ public class StackConveyor extends Block implements Autotiler{ if(!enabled) return; if(state == stateUnload){ //unload - while(lastItem != null && items.has(lastItem) && !outputRouter ? moveForward(lastItem) : dump(lastItem)){ + while(lastItem != null && !outputRouter ? moveForward(lastItem) : dump(lastItem)){ if(!outputRouter){ items.remove(lastItem, 1); } - if(items.empty()){ + if(!items.has(lastItem)){ poofOut(); lastItem = null; + break; } } }else{ //transfer From 2f1f334d65ea8cbd213acdde1c10d2b2e1fe8702 Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 16 Jul 2024 14:17:10 -0400 Subject: [PATCH 322/348] Turret ammo sync --- core/src/mindustry/core/NetClient.java | 2 +- core/src/mindustry/core/NetServer.java | 2 +- core/src/mindustry/entities/comp/BuildingComp.java | 8 ++++++++ .../world/blocks/defense/turrets/Turret.java | 12 ++++++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/core/NetClient.java b/core/src/mindustry/core/NetClient.java index ec6dc2b0fc..8d3ac0bd19 100644 --- a/core/src/mindustry/core/NetClient.java +++ b/core/src/mindustry/core/NetClient.java @@ -478,7 +478,7 @@ public class NetClient implements ApplicationListener{ Log.warn("Block ID mismatch at @: @ != @. Skipping block snapshot.", tile, tile.build.block.id, block); break; } - tile.build.readAll(Reads.get(input), tile.build.version()); + tile.build.readSync(Reads.get(input), tile.build.version()); } }catch(Exception e){ Log.err(e); diff --git a/core/src/mindustry/core/NetServer.java b/core/src/mindustry/core/NetServer.java index e6a7c75de1..5f4be71024 100644 --- a/core/src/mindustry/core/NetServer.java +++ b/core/src/mindustry/core/NetServer.java @@ -913,7 +913,7 @@ public class NetServer implements ApplicationListener{ dataStream.writeInt(entity.pos()); dataStream.writeShort(entity.block.id); - entity.writeAll(Writes.get(dataStream)); + entity.writeSync(Writes.get(dataStream)); if(syncStream.size() > maxSnapshotSize){ dataStream.close(); diff --git a/core/src/mindustry/entities/comp/BuildingComp.java b/core/src/mindustry/entities/comp/BuildingComp.java index 9751fbd4c7..16a9af2fc3 100644 --- a/core/src/mindustry/entities/comp/BuildingComp.java +++ b/core/src/mindustry/entities/comp/BuildingComp.java @@ -261,6 +261,14 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, read(read, revision); } + public void writeSync(Writes write){ + writeAll(write); + } + + public void readSync(Reads read, byte revision){ + readAll(read, revision); + } + @CallSuper public void write(Writes write){ //overriden by subclasses! diff --git a/core/src/mindustry/world/blocks/defense/turrets/Turret.java b/core/src/mindustry/world/blocks/defense/turrets/Turret.java index 0baea25f93..e9f5331bbe 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/Turret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/Turret.java @@ -140,6 +140,7 @@ public class Turret extends ReloadTurret{ quickRotate = false; outlinedIcon = 1; drawLiquidLight = false; + sync = true; } @Override @@ -664,6 +665,17 @@ public class Turret extends ReloadTurret{ public byte version(){ return 1; } + + @Override + public void readSync(Reads read, byte revision){ + //maintain rotation and reload when syncing so clients don't see turrets snapping around + float oldRot = rotation, oldReload = reloadCounter; + + readAll(read, revision); + + rotation = oldRot; + reloadCounter = oldReload; + } } public static class BulletEntry{ From f6c240262d49763e55cb0d11d6ee07d7bc2ed5f8 Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 16 Jul 2024 17:53:27 -0400 Subject: [PATCH 323/348] Fixed incorrect RailBullet length with pierceCap --- core/src/mindustry/entities/Damage.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/mindustry/entities/Damage.java b/core/src/mindustry/entities/Damage.java index 780edb5b3b..988b92660e 100644 --- a/core/src/mindustry/entities/Damage.java +++ b/core/src/mindustry/entities/Damage.java @@ -245,6 +245,7 @@ public class Damage{ */ public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length, boolean large, boolean laser, int pierceCap){ length = findLength(hitter, length, laser, pierceCap); + hitter.fdata = length; collidedBlocks.clear(); vec.trnsExact(angle, length); From 394ed99616569d1d5ffa5be6c8cf1776541b06c8 Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 18 Jul 2024 10:04:36 -0400 Subject: [PATCH 324/348] More item request validation --- core/src/mindustry/input/InputHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index 954a81cdb5..49146f701c 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -405,7 +405,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ @Remote(called = Loc.server, targets = Loc.both, forward = true) public static void requestItem(Player player, Building build, Item item, int amount){ - if(player == null || build == null || !build.interactable(player.team()) || !player.within(build, itemTransferRange) || player.dead()) return; + if(player == null || build == null || !build.interactable(player.team()) || !player.within(build, itemTransferRange) || player.dead() || amount <= 0) return; if(net.server() && (!Units.canInteract(player, build) || !netServer.admins.allowAction(player, ActionType.withdrawItem, build.tile(), action -> { From 10e1680d3b38e1a213be7bbcad5cde0b03801856 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 19 Jul 2024 19:07:10 -0400 Subject: [PATCH 325/348] Fixed #10039 --- core/src/mindustry/ai/types/BuilderAI.java | 2 +- core/src/mindustry/input/DesktopInput.java | 2 +- core/src/mindustry/ui/fragments/HudFragment.java | 2 +- gradle.properties | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/mindustry/ai/types/BuilderAI.java b/core/src/mindustry/ai/types/BuilderAI.java index ff57627c33..ccbe64b7c6 100644 --- a/core/src/mindustry/ai/types/BuilderAI.java +++ b/core/src/mindustry/ai/types/BuilderAI.java @@ -160,7 +160,7 @@ public class BuilderAI extends AIController{ float minDst = Float.MAX_VALUE; Player closest = null; for(var player : Groups.player){ - if(player.unit().canBuild() && !player.dead() && player.team() == unit.team){ + if(!player.dead() && player.isBuilder() && player.team() == unit.team){ float dst = player.dst2(unit); if(dst < minDst){ closest = player; diff --git a/core/src/mindustry/input/DesktopInput.java b/core/src/mindustry/input/DesktopInput.java index 86e635c357..1fc15522d1 100644 --- a/core/src/mindustry/input/DesktopInput.java +++ b/core/src/mindustry/input/DesktopInput.java @@ -572,7 +572,7 @@ public class DesktopInput extends InputHandler{ player.unit().mineTile = null; } - if(Core.input.keyTap(Binding.clear_building)){ + if(Core.input.keyTap(Binding.clear_building) && !player.dead()){ player.unit().clearBuilding(); } diff --git a/core/src/mindustry/ui/fragments/HudFragment.java b/core/src/mindustry/ui/fragments/HudFragment.java index 7e1f78b217..70a1d15279 100644 --- a/core/src/mindustry/ui/fragments/HudFragment.java +++ b/core/src/mindustry/ui/fragments/HudFragment.java @@ -913,7 +913,7 @@ public class HudFragment{ table.table().update(t -> { t.left(); - Bits applied = player.unit() == null ? null : player.unit().statusBits(); + Bits applied = player.dead() ? null : player.unit().statusBits(); if(!statuses.equals(applied)){ t.clear(); diff --git a/gradle.properties b/gradle.properties index a87b03065c..827c8445c4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,4 +26,4 @@ org.gradle.caching=true org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 android.enableR8.fullMode=false -archash=b857594d11 +archash=96cd86d08a From 571301cb04a8d1a86ca5305bf662aed77997d73a Mon Sep 17 00:00:00 2001 From: a-big-fish-fish <111189982+a-big-fish-fish@users.noreply.github.com> Date: Sat, 20 Jul 2024 07:13:36 +0800 Subject: [PATCH 326/348] Update servers_v7.json (#10040) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 70123db09f..89e7a76989 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -254,7 +254,7 @@ }, { "name": "Extra Utilities", - "address": ["p1.i9mr.com:44922", "p1.i9mr.com:44834", "p1.i9mr.com:43189"] + "address": ["p1.i9mr.com:44922", "p1.i9mr.com:44834", "p1.i9mr.com:43189", "203.135.99.189:15142", "203.135.99.189:15143"] }, { "name": "Alex Multiverse", From c2f276d7533b77410603e1ef09db3103bae6010f Mon Sep 17 00:00:00 2001 From: Nuriake Date: Sat, 20 Jul 2024 06:16:22 +0700 Subject: [PATCH 327/348] Update servers_v7.json (#10038) * Update servers_v7.json * Update servers_v7.json --- servers_v7.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/servers_v7.json b/servers_v7.json index 89e7a76989..aef87a4095 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -313,8 +313,8 @@ "address": ["194.247.42.131:27715", "194.247.42.131:27512"] }, { - "name": "SandBox", - "address": ["185.9.145.8:25993"] + "name": "Erbium", + "address": ["hp5.nexcord.com:12066", "hp5.nexcord.com:11999", "hp5.nexcord.com:12067"] }, { "name": "Vndustry", From 0a2d8c20be9077ee989e90e28bd9956a81e748b4 Mon Sep 17 00:00:00 2001 From: Vainer <57346251+Vainer-prog@users.noreply.github.com> Date: Sat, 20 Jul 2024 06:16:42 +0700 Subject: [PATCH 328/348] Update servers_v7.json (#10030) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index aef87a4095..e78b670a92 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -322,7 +322,7 @@ }, { "name": "ChillOut", - "address": ["87.228.8.133"] + "address": ["87.228.8.133", "87.228.8.133:6568"] }, { "name": "VNM", From f7d91eb8c93c68c734226b0e60615dc8ef54b24a Mon Sep 17 00:00:00 2001 From: Joan Josep Date: Sat, 20 Jul 2024 01:16:52 +0200 Subject: [PATCH 329/348] Update bundle_ca.properties (#10032) --- core/assets/bundles/bundle_ca.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index bcee969240..d3b21861c3 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -1121,8 +1121,8 @@ category.items = Elements category.crafting = Entrada/Sortida category.function = Funcionament category.optional = Millores opcionals -setting.alwaysmusic.name = Always Play Music -setting.alwaysmusic.description = When enabled, music will always play on loop in-game.\nWhen disabled, it only plays at random intervals. +setting.alwaysmusic.name = Reprodueix música sempre +setting.alwaysmusic.description = Quan està activat, la música es reproduirà en bucle durant les partides.\Quan està desactivat, només es reproduirà a intervals aleatoris. setting.skipcoreanimation.name = Omet l’animació del llançament i aterratge del nucli setting.landscape.name = Bloca el paisatge setting.shadows.name = Ombres @@ -2400,7 +2400,7 @@ lenum.shoot = Dispara a una posició. lenum.shootp = Dispara a una unitat/bloc tenint en compte la seva velocitat a l’hora d’apuntar. lenum.config = Configuració de l’estructura, com ara el classificador. lenum.enabled = Retorna si el bloc està activat. -laccess.currentammotype = Current ammo item/liquid of a turret. +laccess.currentammotype = Líquid o element de munició actual de la torreta. laccess.color = Color de l’il·luminador. laccess.controller = Controlador de la unitat. Si es controla per processador, retorna el processador.\nAltrament, retorna la mateixa unitat. From cc9a3d53d923cde78e86016a1ef4ada084179ece Mon Sep 17 00:00:00 2001 From: summoner001 Date: Sat, 20 Jul 2024 01:17:02 +0200 Subject: [PATCH 330/348] Update bundle_hu.properties (#10036) Minor fixes. --- core/assets/bundles/bundle_hu.properties | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index b4bc215ef8..b8a1a68223 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -93,7 +93,7 @@ stats.deconstructed = Építmények lebontva stats.playtime = Játékban töltött idő globalitems = [accent]A bolygó nyersanyagai -map.delete = Biztosan törölni akarod a(z) „[accent]{0}[]” pályát? +map.delete = Biztosan törölni akarod a(z) „[accent]{0}[]” nevű pályát? level.highscore = Legmagasabb pontszám: [accent]{0} level.select = Szint kiválasztása level.mode = Játékmód: @@ -285,13 +285,13 @@ server.outdated = [scarlet]Elavult kiszolgáló![] server.outdated.client = [scarlet]Elavult kliens![] server.version = [gray]v{0} {1} server.custombuild = [accent]Saját összeállítás -confirmban = Biztosan tiltod „{0}[white]” játékost? -confirmkick = Biztosan kirúgod „{0}[white]” játékost? +confirmban = Biztosan tiltod a(z) „{0}[white]” nevű játékost? +confirmkick = Biztosan kirúgod a(z) „{0}[white]” nevű játékost? confirmunban = Biztosan újra engedélyezed ezt a játékost? -confirmadmin = Biztosan előlépteted „{0}[white]” játékost adminná? -confirmunadmin = Biztosan meg akarod szüntetni „{0}[white]” játékos adminisztrátori státuszát? +confirmadmin = Biztosan előlépteted a(z) „{0}[white]” nevű játékost adminná? +confirmunadmin = Biztosan meg akarod szüntetni a(z) „{0}[white]” nevű játékos adminisztrátori státuszát? votekick.reason = Kiszavazás oka -votekick.reason.message = Valóban ki akarod szavazni „{0}[white]” játékost?\nHa igen, írd be az okát: +votekick.reason.message = Valóban ki akarod szavazni a(z) „{0}[white]” nevű játékost?\nHa igen, írd be az okát: joingame.title = Kapcsolódás a játékhoz joingame.ip = Cím: disconnect = Kapcsolat bontva. @@ -536,7 +536,7 @@ editor.savechanges = [scarlet]Nem mentett módosításaid vannak!\n\n[]Szeretné editor.saved = Mentve! editor.save.noname = A pályádnak nincs neve! Állíts be egyet a „pályainformációk” menüben. editor.save.overwrite = A pályád felülír egy beépített pályát! Válassz egy másik nevet a „pályainformációk” menüben. -editor.import.exists = [scarlet]Nem lehet importálni:[] Már létezik „{0}” nevű beépített pálya! +editor.import.exists = [scarlet]Nem lehet importálni:[] Már létezik a(z) „{0}” nevű beépített pálya! editor.import = Importálás... editor.importmap = Pálya importálása editor.importmap.description = Létező pálya importálása @@ -1253,7 +1253,7 @@ keybind.unit_stance_ram.name = Egység viselkedése: ütközés keybind.unit_command_move.name = Egységparancs: mozgás keybind.unit_command_repair.name = Egységparancs: javítás -keybind.unit_command_rebuild.name = Egységparancs: újjáépítés +keybind.unit_command_rebuild.name = Egységparancs: újraépítés keybind.unit_command_assist.name = Egységparancs: támogatás keybind.unit_command_mine.name = Egységparancs: bányászás keybind.unit_command_boost.name = Egységparancs: erősítés @@ -1262,7 +1262,7 @@ keybind.unit_command_load_blocks.name = Egységparancs: blokkok berakodása keybind.unit_command_unload_payload.name = Egységparancs: kirakodás keybind.unit_command_enter_payload.name = Egységparancs: berakodás -keybind.rebuild_select.name = Régió újjáépítése +keybind.rebuild_select.name = Régió újraépítése keybind.schematic_select.name = Terület kijelölése keybind.schematic_menu.name = Vázlat menü keybind.schematic_flip_x.name = Vázlat tükrözése vízszintesen @@ -1866,7 +1866,7 @@ block.tank-refabricator.name = Tankújratervező block.mech-refabricator.name = Mechújratervező block.ship-refabricator.name = Repülőgép-újratervező block.tank-assembler.name = Tankösszeszerelő -block.ship-assembler.name = Hajó-összeszerelő +block.ship-assembler.name = Repülőgép-összeszerelő block.mech-assembler.name = Mechösszeszerelő block.reinforced-payload-conveyor.name = Megerősített rakományszállító-szalag block.reinforced-payload-router.name = Megerősített rakományelosztó @@ -1925,7 +1925,7 @@ hint.launch = Ha elegendő nyersanyagot gyűjtöttél össze, akkor [accent]lőd hint.launch.mobile = Ha elegendő nyersanyagot gyűjtöttél össze, akkor [accent]lődd ki[] el a támaszpontot egy közeli szektorba, úgy, hogy kiválasztasz egy szektort a \ue88c [accent]Menüben[] a \ue827 [accent]Bolygótérképről[]. hint.schematicSelect = Tartsd nyomja az [accent][[F][] gombot több épület kijelöléséhez és másolásához.\n\n[accent][[Középső kattintással][] egy adott blokktípus másolható. hint.rebuildSelect = Tartsd nyomva a [accent][[B][] gombot és húzással jelöld ki a megsemmisített blokkterveket.\nEz automatikusan újraépíti őket. -hint.rebuildSelect.mobile = Válaszd a \ue874 másolás gombot, majd koppints az \ue80f újjáépítés gombra, és húzd a megsemmisült blokktervek kijelöléséhez.\nEz automatikusan újraépíti őket. +hint.rebuildSelect.mobile = Válaszd a \ue874 másolás gombot, majd koppints az \ue80f újraépítés gombra, és húzd a megsemmisült blokktervek kijelöléséhez.\nEz automatikusan újraépíti őket. hint.conveyorPathfind = Tartsd nyomva a [accent][[bal Ctrl][] gombot a szállítószalagok lerakása közben, hogy a játék útvonalat állítson elő. hint.conveyorPathfind.mobile = Engedélyezd az \ue844 [accent]átlós módot[], és tegyél le egyszerre több szállítószalagot, hogy a játék útvonalat állítson elő. hint.boost = Tartsd nyomva a [accent][[bal Shift][] gombot, hogy átrepülj az akadályok felett.\n\nErre csak néhány földi egység képes. @@ -2266,10 +2266,10 @@ block.tank-fabricator.description = Stell egységeket épít. Az elkészült egy block.ship-fabricator.description = Elude egységeket épít. Az elkészült egységek azonnal hadra foghatóak, vagy újratervezőkben továbbfejleszthetőek. block.mech-fabricator.description = Merui egységeket épít. Az elkészült egységek azonnal hadra foghatóak, vagy újratervezőkben továbbfejleszthetőek. block.tank-assembler.description = Nagy méretű tankokat állít össze a beadott blokkokból és egységekből. A kimeneti szint modulok hozzáadásával növelhető. -block.ship-assembler.description = Nagy méretű hajókat állít össze a beadott blokkokból és egységekből. A kimeneti szint modulok hozzáadásával növelhető. +block.ship-assembler.description = Nagy méretű repülőgépeket állít össze a beadott blokkokból és egységekből. A kimeneti szint modulok hozzáadásával növelhető. block.mech-assembler.description = Nagy méretű mecheket állít össze a beadott blokkokból és egységekből. A kimeneti szint modulok hozzáadásával növelhető. block.tank-refabricator.description = Kettes szintre fejleszti a beérkező tank típusú egységeket. -block.ship-refabricator.description = Kettes szintre fejleszti a beérkező hajó típusú egységeket. +block.ship-refabricator.description = Kettes szintre fejleszti a beérkező repülőgép típusú egységeket. block.mech-refabricator.description = Kettes szintre fejleszti a beérkező mech típusú egységeket. block.prime-refabricator.description = Hármas szintre fejleszti a beérkező tank típusú egységeket. block.basic-assembler-module.description = Növeli az összeszerelő szintjét, ha annak az építési határvonala mellé helyezik. Áramot igényel. Használható nyersanyagrakomány-bemenetként. @@ -2563,7 +2563,7 @@ unitlocate.outy = Kimenet Y koordinátája. unitlocate.group = Keresendő épületcsoport. lenum.idle = Ne mozdulj, de folytasd az építkezést/bányászatot.\nAz alapértelmezett állapot. -lenum.stop = Mozgás/bányászat/építés leállítása. +lenum.stop = Mozgás/bányászás/építés leállítása. lenum.unbind = A logikai vezérlés teljes kikapcsolása.\nSzokásos mesterséges intelligencia folytatása. lenum.move = Mozgás a pontos pozícióba. lenum.approach = Egy pozíció megközelítése egy sugárral. From 0d51aa8ebbe44248789ea47ff5852ba8aa46b1d9 Mon Sep 17 00:00:00 2001 From: GeoNew2011 <152255543+GeoNew2011@users.noreply.github.com> Date: Sat, 20 Jul 2024 02:17:13 +0300 Subject: [PATCH 331/348] Update servers_v7.json (#10037) +siege +test --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index e78b670a92..9bad8a60de 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -286,7 +286,7 @@ }, { "name": "Erepulo", - "address": ["95.84.198.97:5401", "95.84.198.97:5402", "95.84.198.97:5403", "95.84.198.97:2357"] + "address": ["95.84.198.97:5401", "95.84.198.97:5402", "95.84.198.97:5403", "95.84.198.97:5404", "95.84.198.97:2357", "95.84.198.97:5500"] }, { "name": "MineCore", From 047aa4fdaa277d82695b800f4b30fb67c5f0363d Mon Sep 17 00:00:00 2001 From: Github Actions Date: Fri, 19 Jul 2024 23:17:59 +0000 Subject: [PATCH 332/348] Automatic bundle update --- core/assets/bundles/bundle_ca.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index d3b21861c3..3be98ea31b 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -1122,7 +1122,7 @@ category.crafting = Entrada/Sortida category.function = Funcionament category.optional = Millores opcionals setting.alwaysmusic.name = Reprodueix música sempre -setting.alwaysmusic.description = Quan està activat, la música es reproduirà en bucle durant les partides.\Quan està desactivat, només es reproduirà a intervals aleatoris. +setting.alwaysmusic.description = Quan està activat, la música es reproduirà en bucle durant les partides.Quan està desactivat, només es reproduirà a intervals aleatoris. setting.skipcoreanimation.name = Omet l’animació del llançament i aterratge del nucli setting.landscape.name = Bloca el paisatge setting.shadows.name = Ombres From 0a224c52b405b59268bbbf100a6a2811dd75bb2e Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 21 Jul 2024 07:53:34 -0400 Subject: [PATCH 333/348] Fixed #10042 --- core/src/mindustry/entities/comp/BuildingComp.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/mindustry/entities/comp/BuildingComp.java b/core/src/mindustry/entities/comp/BuildingComp.java index 16a9af2fc3..067ebd0afd 100644 --- a/core/src/mindustry/entities/comp/BuildingComp.java +++ b/core/src/mindustry/entities/comp/BuildingComp.java @@ -59,6 +59,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, @Import float x, y, health, maxHealth; @Import Team team; + @Import boolean dead; transient Tile tile; transient Block block; @@ -2081,6 +2082,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, @Override public void killed(){ + dead = true; Events.fire(new BlockDestroyEvent(tile)); block.destroySound.at(tile); onDestroyed(); From 6e5b1e81aed490e2b87573719aea0e229f195f7f Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 23 Jul 2024 07:21:05 -0400 Subject: [PATCH 334/348] Crash fix --- core/src/mindustry/entities/comp/PlayerComp.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/entities/comp/PlayerComp.java b/core/src/mindustry/entities/comp/PlayerComp.java index 0ed2a11ad4..9ea4ccf30e 100644 --- a/core/src/mindustry/entities/comp/PlayerComp.java +++ b/core/src/mindustry/entities/comp/PlayerComp.java @@ -297,7 +297,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra font.getData().setScale(0.25f / Scl.scl(1f)); layout.setText(font, name); - if(!isLocal()){ + if(!isLocal() && unit != null){ Draw.color(0f, 0f, 0f, 0.3f); Fill.rect(unit.x, unit.y + nameHeight - layout.height / 2, layout.width + 2, layout.height + 3); Draw.color(); @@ -313,7 +313,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra } } - if(Core.settings.getBool("playerchat") && ((textFadeTime > 0 && lastText != null) || typing)){ + if(unit != null && Core.settings.getBool("playerchat") && ((textFadeTime > 0 && lastText != null) || typing)){ String text = textFadeTime <= 0 || lastText == null ? "[lightgray]" + Strings.animated(Time.time, 4, 15f, ".") : lastText; float width = 100f; float visualFadeTime = 1f - Mathf.curve(1f - textFadeTime, 0.9f); From 995014cbe4df6eaa3974c1ed8725ca1598c5cf8b Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 23 Jul 2024 07:26:17 -0400 Subject: [PATCH 335/348] Cleanup --- core/src/mindustry/entities/comp/PlayerComp.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/core/src/mindustry/entities/comp/PlayerComp.java b/core/src/mindustry/entities/comp/PlayerComp.java index 9ea4ccf30e..834d4c239a 100644 --- a/core/src/mindustry/entities/comp/PlayerComp.java +++ b/core/src/mindustry/entities/comp/PlayerComp.java @@ -279,10 +279,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra @Override public void draw(){ - if(unit != null && unit.inFogTo(Vars.player.team())) return; - - //?????? - if(name == null) return; + if(unit == null || name == null || unit.inFogTo(Vars.player.team())) return; Draw.z(Layer.playerName); float z = Drawf.text(); @@ -297,7 +294,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra font.getData().setScale(0.25f / Scl.scl(1f)); layout.setText(font, name); - if(!isLocal() && unit != null){ + if(!isLocal()){ Draw.color(0f, 0f, 0f, 0.3f); Fill.rect(unit.x, unit.y + nameHeight - layout.height / 2, layout.width + 2, layout.height + 3); Draw.color(); @@ -313,7 +310,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra } } - if(unit != null && Core.settings.getBool("playerchat") && ((textFadeTime > 0 && lastText != null) || typing)){ + if(Core.settings.getBool("playerchat") && ((textFadeTime > 0 && lastText != null) || typing)){ String text = textFadeTime <= 0 || lastText == null ? "[lightgray]" + Strings.animated(Time.time, 4, 15f, ".") : lastText; float width = 100f; float visualFadeTime = 1f - Mathf.curve(1f - textFadeTime, 0.9f); From a594d796ad5f56bfba453419f9bd78dd6980db8c Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 23 Jul 2024 11:31:34 -0400 Subject: [PATCH 336/348] Batch optimizations --- core/src/mindustry/ClientLauncher.java | 2 +- core/src/mindustry/entities/effect/ParticleEffect.java | 4 ++-- core/src/mindustry/graphics/Drawf.java | 2 +- core/src/mindustry/graphics/LightRenderer.java | 2 +- core/src/mindustry/type/Weather.java | 2 +- .../src/mindustry/world/blocks/distribution/ItemBridge.java | 6 ++++-- core/src/mindustry/world/blocks/storage/CoreBlock.java | 2 +- gradle.properties | 2 +- 8 files changed, 12 insertions(+), 10 deletions(-) diff --git a/core/src/mindustry/ClientLauncher.java b/core/src/mindustry/ClientLauncher.java index 2a7b133588..2e8d7396c1 100644 --- a/core/src/mindustry/ClientLauncher.java +++ b/core/src/mindustry/ClientLauncher.java @@ -70,7 +70,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform }); UI.loadColors(); - batch = new SortedSpriteBatch(); + batch = new SpriteBatch(); assets = new AssetManager(); assets.setLoader(Texture.class, "." + mapExtension, new MapPreviewLoader()); diff --git a/core/src/mindustry/entities/effect/ParticleEffect.java b/core/src/mindustry/entities/effect/ParticleEffect.java index 4da451b08f..65e376babc 100644 --- a/core/src/mindustry/entities/effect/ParticleEffect.java +++ b/core/src/mindustry/entities/effect/ParticleEffect.java @@ -79,7 +79,7 @@ public class ParticleEffect extends Effect{ float x = rv.x, y = rv.y; Lines.lineAngle(ox + x, oy + y, Mathf.angle(x, y), len, cap); - Drawf.light(ox + x, oy + y, len * lightScl, lightColor, lightOpacity * Draw.getColor().a); + Drawf.light(ox + x, oy + y, len * lightScl, lightColor, lightOpacity * Draw.getColorAlpha()); } }else{ rand.setSeed(e.id); @@ -89,7 +89,7 @@ public class ParticleEffect extends Effect{ float x = rv.x, y = rv.y; Draw.rect(tex, ox + x, oy + y, rad, rad / tex.ratio(), realRotation + offset + e.time * spin); - Drawf.light(ox + x, oy + y, rad * lightScl, lightColor, lightOpacity * Draw.getColor().a); + Drawf.light(ox + x, oy + y, rad * lightScl, lightColor, lightOpacity * Draw.getColorAlpha()); } } } diff --git a/core/src/mindustry/graphics/Drawf.java b/core/src/mindustry/graphics/Drawf.java index 44cbc6b38e..db7db61dd5 100644 --- a/core/src/mindustry/graphics/Drawf.java +++ b/core/src/mindustry/graphics/Drawf.java @@ -480,7 +480,7 @@ public class Drawf{ /** Draws a sprite that should be light-wise correct, when rotated. Provided sprite must be symmetrical in shape. */ public static void spinSprite(TextureRegion region, float x, float y, float r){ - float a = Draw.getColor().a; + float a = Draw.getColorAlpha(); r = Mathf.mod(r, 90f); Draw.rect(region, x, y, r); Draw.alpha(r / 90f*a); diff --git a/core/src/mindustry/graphics/LightRenderer.java b/core/src/mindustry/graphics/LightRenderer.java index a0cab5c03e..7c30b94796 100644 --- a/core/src/mindustry/graphics/LightRenderer.java +++ b/core/src/mindustry/graphics/LightRenderer.java @@ -70,7 +70,7 @@ public class LightRenderer{ float rot = Mathf.angleExact(x2 - x, y2 - y); TextureRegion ledge = Core.atlas.find("circle-end"), lmid = Core.atlas.find("circle-mid"); - float color = Draw.getColor().toFloatBits(); + float color = Draw.getColorPacked(); float u = lmid.u; float v = lmid.v2; float u2 = lmid.u2; diff --git a/core/src/mindustry/type/Weather.java b/core/src/mindustry/type/Weather.java index d8b7618ccc..e70e0af9af 100644 --- a/core/src/mindustry/type/Weather.java +++ b/core/src/mindustry/type/Weather.java @@ -161,7 +161,7 @@ public class Weather extends UnlockableContent{ Core.camera.bounds(Tmp.r2); int total = (int)(Tmp.r1.area() / density * intensity); Lines.stroke(stroke); - float alpha = Draw.getColor().a; + float alpha = Draw.getColorAlpha(); Draw.color(color); for(int i = 0; i < total; i++){ diff --git a/core/src/mindustry/world/blocks/distribution/ItemBridge.java b/core/src/mindustry/world/blocks/distribution/ItemBridge.java index 2663365fc2..0ec2e16bcd 100644 --- a/core/src/mindustry/world/blocks/distribution/ItemBridge.java +++ b/core/src/mindustry/world/blocks/distribution/ItemBridge.java @@ -238,13 +238,15 @@ public class ItemBridge extends Block{ Lines.stroke(2.5f); Lines.line(tx + Tmp.v2.x, ty + Tmp.v2.y, ox - Tmp.v2.x, oy - Tmp.v2.y); + float color = (linked ? Pal.place : Pal.accent).toFloatBits(); + //draw foreground colors - Draw.color(linked ? Pal.place : Pal.accent); + Draw.color(color); Lines.stroke(1f); Lines.line(tx + Tmp.v2.x, ty + Tmp.v2.y, ox - Tmp.v2.x, oy - Tmp.v2.y); Lines.square(ox, oy, 2f, 45f); - Draw.mixcol(Draw.getColor(), 1f); + Draw.mixcol(color); Draw.color(); Draw.rect(arrowRegion, x, y, rel * 90); Draw.mixcol(); diff --git a/core/src/mindustry/world/blocks/storage/CoreBlock.java b/core/src/mindustry/world/blocks/storage/CoreBlock.java index c3d50f69d7..15a80f0c6e 100644 --- a/core/src/mindustry/world/blocks/storage/CoreBlock.java +++ b/core/src/mindustry/world/blocks/storage/CoreBlock.java @@ -292,7 +292,7 @@ public class CoreBlock extends StorageBlock{ protected void drawLandingThrusters(float x, float y, float rotation, float frame){ float length = thrusterLength * (frame - 1f) - 1f/4f; - float alpha = Draw.getColor().a; + float alpha = Draw.getColorAlpha(); //two passes for consistent lighting for(int j = 0; j < 2; j++){ diff --git a/gradle.properties b/gradle.properties index 827c8445c4..f4f892e59f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,4 +26,4 @@ org.gradle.caching=true org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 android.enableR8.fullMode=false -archash=96cd86d08a +archash=8055a9104d From 5ac90d6ed32366cb5b3d3428261c062a632ba2fe Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 23 Jul 2024 11:55:09 -0400 Subject: [PATCH 337/348] Minor power node optimizations --- core/src/mindustry/world/blocks/power/PowerNode.java | 3 +-- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/mindustry/world/blocks/power/PowerNode.java b/core/src/mindustry/world/blocks/power/PowerNode.java index 582a2246a1..421684f43b 100644 --- a/core/src/mindustry/world/blocks/power/PowerNode.java +++ b/core/src/mindustry/world/blocks/power/PowerNode.java @@ -173,8 +173,7 @@ public class PowerNode extends PowerBlock{ } protected void setupColor(float satisfaction){ - Draw.color(laserColor1, laserColor2, (1f - satisfaction) * 0.86f + Mathf.absin(3f, 0.1f)); - Draw.alpha(Renderer.laserOpacity); + Draw.color(Tmp.c1.set(laserColor1).lerp(laserColor2, (1f - satisfaction) * 0.86f + Mathf.absin(3f, 0.1f)).a(Renderer.laserOpacity)); } public void drawLaser(float x1, float y1, float x2, float y2, int size1, int size2){ diff --git a/gradle.properties b/gradle.properties index f4f892e59f..33140fd7d2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,4 +26,4 @@ org.gradle.caching=true org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 android.enableR8.fullMode=false -archash=8055a9104d +archash=010cf80f67 From f29bf8ca851d29fe955396732c858485b0a124e8 Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 23 Jul 2024 18:35:13 -0400 Subject: [PATCH 338/348] arc --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 33140fd7d2..d3e15e0aac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,4 +26,4 @@ org.gradle.caching=true org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 android.enableR8.fullMode=false -archash=010cf80f67 +archash=2986baf556 From e604c3b098d3f639eee7d25aa8bd3805c82cf3ab Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 26 Jul 2024 22:23:01 -0400 Subject: [PATCH 339/348] Clean tiles on reset --- core/src/mindustry/core/Logic.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/mindustry/core/Logic.java b/core/src/mindustry/core/Logic.java index 7d1a7bb5b9..59f9b3cdcd 100644 --- a/core/src/mindustry/core/Logic.java +++ b/core/src/mindustry/core/Logic.java @@ -261,6 +261,7 @@ public class Logic implements ApplicationListener{ Groups.clear(); Time.clear(); Events.fire(new ResetEvent()); + world.tiles = new Tiles(0, 0); //save settings on reset Core.settings.manualSave(); From 3a1a06e57fe1622a1ced2ea23608344f521f9846 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 26 Jul 2024 22:30:12 -0400 Subject: [PATCH 340/348] Fixed #10051 --- core/src/mindustry/core/NetServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/core/NetServer.java b/core/src/mindustry/core/NetServer.java index 5f4be71024..3558877cbb 100644 --- a/core/src/mindustry/core/NetServer.java +++ b/core/src/mindustry/core/NetServer.java @@ -205,7 +205,7 @@ public class NetServer implements ApplicationListener{ info.id = packet.uuid; admins.save(); Call.infoMessage(con, "You are not whitelisted here."); - info("&lcDo &lywhitelist-add @&lc to whitelist the player &lb'@'", packet.uuid, packet.name); + info("&lcDo &lywhitelist add @&lc to whitelist the player &lb'@'", packet.uuid, packet.name); con.kick(KickReason.whitelist); return; } From 885ca555a838e89c75f3de5eb895b5c93cb52893 Mon Sep 17 00:00:00 2001 From: jadfoq Date: Sat, 27 Jul 2024 05:38:48 +0300 Subject: [PATCH 341/348] Add server -- servers_v7.json (#10049) --- servers_v7.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/servers_v7.json b/servers_v7.json index 9bad8a60de..3a337e563f 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -327,5 +327,9 @@ { "name": "VNM", "address": ["server.mindustry-tool.app"] + }, + { + "name": "LibreDUSTRY", + "address": ["46.4.114.111"] } ] From 733f12a6054c43d0eb0f7cd7137294b7407b100a Mon Sep 17 00:00:00 2001 From: summoner001 Date: Sat, 27 Jul 2024 04:38:59 +0200 Subject: [PATCH 342/348] Update bundle_hu.properties (#10060) * Update bundle_hu.properties Fixing statistics texts. Formatted to the size of the in-game text box. Now the text does not slip in the game. Tested. * Update bundle_hu.properties Minor translation fix. * Update bundle_hu.properties correct a meaningless sentence * Update bundle_hu.properties correct sentence * Update bundle_hu.properties Use of the same words for flying units. --- core/assets/bundles/bundle_hu.properties | 30 ++++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index b8a1a68223..3e473f5877 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -84,12 +84,12 @@ schematic.tagdelconfirm = Teljesen törlöd ezt a címkét? schematic.tagexists = Ez a címke már létezik. stats = Statisztika -stats.wave = Hullámok legyőzve -stats.unitsCreated = Egységek létrehozva -stats.enemiesDestroyed = Ellenségek megsemmisítve -stats.built = Építmények építve -stats.destroyed = Építmények elpusztítva -stats.deconstructed = Építmények lebontva +stats.wave = Túlélt hullámok +stats.unitsCreated = Létrehozott egységek +stats.enemiesDestroyed = Legyőzött egységek +stats.built = Épített építmények +stats.destroyed = Lerombolt építmények +stats.deconstructed = Lebontott építmények stats.playtime = Játékban töltött idő globalitems = [accent]A bolygó nyersanyagai @@ -869,7 +869,7 @@ sector.atlas.description = Ez a szektor változatos terepet tartalmaz, és az ü sector.split.description = A minimális ellenséges jelenlét miatt ez a szektor tökéletes az új nyersanyagszállító technológiák tesztelésére. sector.basin.description = Jelentős ellenséges jelenlét lett érzékelve ebben a szektorban.\nÉpíts gyorsan egységeket, és foglald el az ellenséges támaszpontokat, hogy megvethesd a lábad. sector.marsh.description = Ebben a szektorban rengeteg arkicit található, de kevés a kürtő.\nÉpíts [accent]Kémiai égetőkamrát[] az áramfejlesztéshez. -sector.peaks.description = A hegyvidéki terep ebben a szektorban a legtöbb egységet használhatatlanná teszi. Repülő egységekre lesz szükség.\nVigyázz az ellenséges légvédelmi létesítményekkel. Lehetséges, hogy az ilyen létesítményeket hatástalanítani lehet a támogató épületeik célba vételével. +sector.peaks.description = A hegyvidéki terep ebben a szektorban a legtöbb egységet használhatatlanná teszi. Légi egységekre lesz szükség.\nVigyázz az ellenséges légvédelmi létesítményekkel. Lehetséges, hogy az ilyen létesítményeket hatástalanítani lehet a támogató épületeik célba vételével. sector.ravine.description = A szektorban nem észlelhető ellenséges támaszpont, de ez egy fontos szállítási útvonal az ellenség számára, így változatos ellenséges erőkkel kell számolni.\nTermelj [accent]elektrometált[]. Építs [accent]Afflict[] lövegtornyokat. sector.caldera-erekir.description = Ebben a szektorban a feltárható nyersanyagok több szigeten szétszóródva találhatóak.\nFejleszd ki és helyezd üzembe a drónalapú szállítmányozást. sector.stronghold.description = A nagy ellenséges tábor ebben a szektorban jelentős mennyiségű [accent]tóriumot[] őriz.\nHasználd magasabb szintű egységek és lövegtornyok fejlesztésére. @@ -939,7 +939,7 @@ stat.opposites = Ellentettek stat.powercapacity = Maximális tárolási kapacitás stat.powershot = Áram/lövés stat.damage = Sebzés -stat.targetsair = Repülő célpontok +stat.targetsair = Légi célpontok stat.targetsground = Földi célpontok stat.itemsmoved = Szállítási sebesség stat.launchtime = Kilövések közötti idő @@ -1174,7 +1174,7 @@ setting.screenshake.name = Képernyő rázkódása setting.bloomintensity.name = Bloom intenzitása setting.bloomblur.name = Bloom elmosása setting.effects.name = Hatások megjelenítése -setting.destroyedblocks.name = Elpusztított blokkok megjelenítése +setting.destroyedblocks.name = Lerombolt építmények megjelenítése setting.blockstatus.name = Blokkok állapotának megjelenítése setting.conveyorpathfinding.name = Szállítószalag útvonalkeresése építéskor setting.sensitivity.name = Kontroller érzékenysége @@ -1719,7 +1719,7 @@ block.duct-bridge.name = Szállítószalaghíd block.large-payload-mass-driver.name = Nagy rakomány-tömegmozgató block.payload-void.name = Rakománynyelő block.payload-source.name = Rakományforrás -block.disassembler.name = Szétszerelő +block.disassembler.name = Szétválasztó block.silicon-crucible.name = Szilíciumolvasztó block.overdrive-dome.name = Túlhajtó búra block.interplanetary-accelerator.name = Bolygóközi gyorsító @@ -1956,7 +1956,7 @@ gz.turrets = Fejleszd ki, és építs két \uf861 [accent]Duo[] lövegtornyot, h gz.duoammo = Szállítószalagok segítségével lásd el [accent]rézzel[] a Duo lövegtornyokat. gz.walls = A [accent]falak[] megakadályozhatják, hogy az épületekben károk keletkezzenek.\nÉpíts \uf8ae [accent]Rézfalakat[] a lövegtornyok köré. gz.defend = Az ellenség közeledik, készülj fel a védekezésre. -gz.aa = A repülő egységeket nem lehet könnyen elintézni a hagyományos lövegtornyokkal.\nA \uf860 [accent]Scatter[] lövegtornyok kiváló légelhárítást biztosítanak, de lőszerként \uf837 [accent]ólomra[] van szükségük. +gz.aa = A légi egységeket nem lehet könnyen elintézni a hagyományos lövegtornyokkal.\nA \uf860 [accent]Scatter[] lövegtornyok kiváló légelhárítást biztosítanak, de lőszerként \uf837 [accent]ólomra[] van szükségük. gz.scatterammo = Szállítószalagok segítségével lásd el \uf837 [accent]ólommal[] a Scatter lövegtornyokat. gz.supplyturret = [accent]Lövegtorony ellátása gz.zone1 = Ez az ellenség leszállóhelye. @@ -2045,7 +2045,7 @@ block.message.description = Üzenetet tárol a szövetségesek kommunikációjá block.reinforced-message.description = Üzenetet tárol a szövetségesek közötti kommunikációhoz. block.world-message.description = A pályakészítésben használható üzenetblokk. Nem lehet megsemmisíteni. block.graphite-press.description = Grafittá préseli a szenet. -block.multi-press.description = Grafittá sajtolja a szenet. Hűtése vizet igényel. +block.multi-press.description = Grafittá sajtolja a szenet. Hűtéséhez viz szükséges. block.silicon-smelter.description = A homokot és a szenet szilíciummá finomítja. block.kiln.description = Ólomüveget olvaszt az ólomból és a homokból. block.plastanium-compressor.description = Olaj és titán felhasználásával műanyagot gyárt. @@ -2148,7 +2148,7 @@ block.unloader.description = Kirakodja a szomszédos épületekből a kiválaszt block.launch-pad.description = Nyersanyagokat juttat el más szektorokba. block.launch-pad.details = Szuborbitális rendszer a nyersanyagok szektorok között történő szállítására. A teherkapszulák törékenyek, ezért nem képesek túlélni a légkörbe való visszatérést. block.duo.description = Változatos lövedékekkel lő az ellenségre. -block.scatter.description = Ólom-, törmelék- vagy ólomüvegdarabokat lő az ellenséges légijárművekre. +block.scatter.description = Ólom-, törmelék- vagy ólomüvegdarabokat lő az ellenséges légi egységekre. block.scorch.description = Megégeti az ellenség közeli földi egységeit. Kis távolságra nagyon hatékony. block.hail.description = Kis lövedékeket lő ki nagy távolságokra lévő földi célpontokra. block.wave.description = Folyadékot önt az ellenségre. Eloltja a tüzeket, ha vízzel van ellátva. @@ -2167,7 +2167,7 @@ block.segment.description = Megsemmisíti a beérkező lövedékeket. A lézerre block.parallax.description = Vonónyalábot bocsát ki, amivel magához vonzza, és közben sebzi a légi egységeket. block.tsunami.description = Erős folyadékhullámot lő az ellenségre. Eloltja a tüzeket, ha vízzel van ellátva. block.silicon-crucible.description = Szilíciumot finomít homokból és szénből, piratitot használ kiegészítő hőforrásként. Forró környezetben még hatékonyabb. -block.disassembler.description = Ritka ásványi összetevőket válogat ki a salakból, alacsony hatékonysággal. Képes tóriumot kiválogatni. +block.disassembler.description = Ritka ásványi összetevőire bontja le a salakot, alacsony hatékonysággal. Képes tóriumot kinyerni. block.overdrive-dome.description = Megnöveli a környező épületek termelési sebességét. A működtetése tóritkvarcot és szilíciumot igényel. block.payload-conveyor.description = Nagy méretű terhet mozgat, például gyárakból érkező egységeket. Mágneses. Használható súlytalanságban. block.payload-router.description = Háromfelé osztja szét a beérkező terhet. Rendezőként is szolgál, ha van megadva szűrő. Mágneses. Használható súlytalanságban. @@ -2517,7 +2517,7 @@ lenum.ally = Szövetséges egység. lenum.attacker = Fegyveres egység. lenum.enemy = Ellenséges egység. lenum.boss = Őrző egység. -lenum.flying = Repülő egység. +lenum.flying = Légi egység. lenum.ground = Földi egység. lenum.player = Egy játékos által irányított egység. From 14f82ed560d630122ebfc53ca2de0c4dba555872 Mon Sep 17 00:00:00 2001 From: CuteMiao Xuwen <127962617+XuwenMeimei@users.noreply.github.com> Date: Sat, 27 Jul 2024 10:39:06 +0800 Subject: [PATCH 343/348] Update servers_v7.json (#10053) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 3a337e563f..54003435a6 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -266,7 +266,7 @@ }, { "name": "XuwenHost", - "address": ["mdt.xuwenblock.cn:6567","rack1.rainplay.cn:59196"] + "address": ["mdt.xuwenblock.cn:6567","mdt.nekopark.cloud:6567"] }, { "name": "Atomic", From 66087fabe2b6a59dd9a04728e23fb86fbc4a2121 Mon Sep 17 00:00:00 2001 From: alex <67626131+alexpvpmindustry@users.noreply.github.com> Date: Sat, 27 Jul 2024 10:39:14 +0800 Subject: [PATCH 344/348] update alex V7 servers (#10055) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index 54003435a6..ea97761759 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -258,7 +258,7 @@ }, { "name": "Alex Multiverse", - "address": ["alexmindustryv7.servegame.com:25588", "172.234.80.96:6768", "139.162.41.78:6767", "172.245.187.143:6868", "172.245.187.143:6869", "92.119.127.171:6888", "45.84.59.51:1025", "45.84.59.51:1027"] + "address": ["alexmindustryv7.servegame.com:25588", "172.234.80.96:6768", "139.162.41.78:6767", "172.245.187.143:6868", "172.245.187.143:6869", "92.119.127.171:6888", "181.215.58.13:1025", "181.215.58.13:1027"] }, { "name": "Open PVP", From f267c30e848b0a075e6a0b218065c2199946b3b9 Mon Sep 17 00:00:00 2001 From: a-big-fish-fish <111189982+a-big-fish-fish@users.noreply.github.com> Date: Sat, 27 Jul 2024 10:39:21 +0800 Subject: [PATCH 345/348] Update servers_v7.json (#10059) --- servers_v7.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers_v7.json b/servers_v7.json index ea97761759..613e8bcac1 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -254,7 +254,7 @@ }, { "name": "Extra Utilities", - "address": ["p1.i9mr.com:44922", "p1.i9mr.com:44834", "p1.i9mr.com:43189", "203.135.99.189:15142", "203.135.99.189:15143"] + "address": ["p1.i9mr.com:44922", "p1.i9mr.com:44834", "p1.i9mr.com:43189", "203.135.99.189:15142", "203.135.99.189:15143", "203.135.99.190:15142", "203.135.99.190:15143"] }, { "name": "Alex Multiverse", From 0de1bec554573cf59a682ecc0606c26a7a39a1b8 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 26 Jul 2024 23:29:28 -0400 Subject: [PATCH 346/348] Fixed #10046 --- core/src/mindustry/ai/ControlPathfinder.java | 6 ++++++ core/src/mindustry/ai/RtsAI.java | 14 ++++++++------ core/src/mindustry/entities/bullet/BulletType.java | 11 +++++++++-- .../entities/bullet/PointLaserBulletType.java | 1 + core/src/mindustry/entities/comp/UnitComp.java | 2 +- .../blocks/defense/turrets/ContinuousTurret.java | 6 ++++++ gradle.properties | 2 +- 7 files changed, 32 insertions(+), 10 deletions(-) diff --git a/core/src/mindustry/ai/ControlPathfinder.java b/core/src/mindustry/ai/ControlPathfinder.java index 261d1073b2..be200eecda 100644 --- a/core/src/mindustry/ai/ControlPathfinder.java +++ b/core/src/mindustry/ai/ControlPathfinder.java @@ -1017,6 +1017,12 @@ public class ControlPathfinder implements Runnable{ var nodePath = clusterAstar(request, costId, node, dest); + //no result found, bail out. + if(nodePath == null){ + request.notFound = true; + return; + } + FieldCache cache = fields.get(Pack.longInt(goalPos, costId)); //if true, extra values are added on the sides of existing field cells that face new cells. boolean addingFrontier = true; diff --git a/core/src/mindustry/ai/RtsAI.java b/core/src/mindustry/ai/RtsAI.java index 92025da099..16fcf3ec7a 100644 --- a/core/src/mindustry/ai/RtsAI.java +++ b/core/src/mindustry/ai/RtsAI.java @@ -19,6 +19,7 @@ import mindustry.logic.*; import mindustry.ui.*; import mindustry.world.*; import mindustry.world.blocks.defense.turrets.BaseTurret.*; +import mindustry.world.blocks.defense.turrets.*; import mindustry.world.blocks.storage.*; import mindustry.world.blocks.storage.CoreBlock.*; import mindustry.world.meta.*; @@ -111,7 +112,8 @@ public class RtsAI{ for(var unit : data.units){ if(used.add(unit.id) && unit.isCommandable() && !unit.command().hasCommand() && !unit.command().isAttacking()){ squad.clear(); - data.tree().intersect(unit.x - squadRadius/2f, unit.y - squadRadius/2f, squadRadius, squadRadius, squad); + float rad = squadRadius + unit.hitSize*1.5f; + data.tree().intersect(unit.x - rad/2f, unit.y - rad/2f, rad, rad, squad); squad.truncate(data.team.rules().rtsMaxSquad); @@ -245,7 +247,7 @@ public class RtsAI{ } } - var build = anyDefend ? null : findTarget(ax, ay, units.size, dps, health, units.first().flag == 0); + var build = anyDefend ? null : findTarget(ax, ay, units.size, dps, health, units.first().flag == 0, units.first().isFlying()); if(build != null || anyDefend){ for(var unit : units){ @@ -268,7 +270,7 @@ public class RtsAI{ return anyDefend; } - @Nullable Building findTarget(float x, float y, int total, float dps, float health, boolean checkWeight){ + @Nullable Building findTarget(float x, float y, int total, float dps, float health, boolean checkWeight, boolean air){ if(total < data.team.rules().rtsMinSquad) return null; //flag priority? @@ -290,7 +292,7 @@ public class RtsAI{ targets.truncate(maxTargetsChecked); for(var target : targets){ - weights.put(target, estimateStats(x, y, target.x, target.y, dps, health)); + weights.put(target, estimateStats(x, y, target.x, target.y, dps, health, air)); } var result = targets.min( @@ -312,12 +314,12 @@ public class RtsAI{ } //TODO extremely slow especially with many squads. - float estimateStats(float fromX, float fromY, float x, float y, float selfDps, float selfHealth){ + float estimateStats(float fromX, float fromY, float x, float y, float selfDps, float selfHealth, boolean air){ float[] health = {0f}, dps = {0f}; float extraRadius = 50f; for(var turret : Vars.indexer.getEnemy(data.team, BlockFlag.turret)){ - if(turret instanceof BaseTurretBuild t && Intersector.distanceSegmentPoint(fromX, fromY, x, y, t.x, t.y) <= t.range() + extraRadius){ + if(turret instanceof BaseTurretBuild t && turret.block instanceof Turret tb && ((tb.targetAir && air) || (tb.targetGround && !air)) && Intersector.distanceSegmentPoint(fromX, fromY, x, y, t.x, t.y) <= t.range() + extraRadius){ health[0] += t.health; dps[0] += t.estimateDps(); } diff --git a/core/src/mindustry/entities/bullet/BulletType.java b/core/src/mindustry/entities/bullet/BulletType.java index 85bd903395..74fb8a7365 100644 --- a/core/src/mindustry/entities/bullet/BulletType.java +++ b/core/src/mindustry/entities/bullet/BulletType.java @@ -309,6 +309,8 @@ public class BulletType extends Content implements Cloneable{ /** Color of light emitted by this bullet. */ public Color lightColor = Pal.powerLight; + protected float cachedDps = -1; + public BulletType(float speed, float damage){ this.speed = speed; this.damage = damage; @@ -338,15 +340,20 @@ public class BulletType extends Content implements Cloneable{ /** @return estimated damage per shot. this can be very inaccurate. */ public float estimateDPS(){ + if(cachedDps >= 0f) return cachedDps; + if(spawnUnit != null){ return spawnUnit.estimateDps(); } - float sum = damage + splashDamage*0.75f; + float sum = damage * (pierce ? pierceCap == -1 ? 2 : Mathf.clamp(pierceCap, 1, 2) : 1f) * splashDamage*0.75f; if(fragBullet != null && fragBullet != this){ sum += fragBullet.estimateDPS() * fragBullets / 2f; } - return sum; + for(var other : spawnBullets){ + sum += other.estimateDPS(); + } + return cachedDps = sum; } /** @return maximum distance the bullet this bullet type has can travel. */ diff --git a/core/src/mindustry/entities/bullet/PointLaserBulletType.java b/core/src/mindustry/entities/bullet/PointLaserBulletType.java index 8e96579a5b..ca81ce7ad7 100644 --- a/core/src/mindustry/entities/bullet/PointLaserBulletType.java +++ b/core/src/mindustry/entities/bullet/PointLaserBulletType.java @@ -118,6 +118,7 @@ public class PointLaserBulletType extends BulletType{ } } + @Override public void updateBulletInterval(Bullet b){ if(intervalBullet != null && b.time >= intervalDelay && b.timer.get(2, bulletInterval)){ float ang = b.rotation(); diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index 63f30a4fc8..ffa2bc3592 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -792,6 +792,6 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I @Override @Replace public String toString(){ - return "Unit#" + id() + ":" + type; + return "Unit#" + id() + ":" + type + " (" + x + ", " + y + ")"; } } diff --git a/core/src/mindustry/world/blocks/defense/turrets/ContinuousTurret.java b/core/src/mindustry/world/blocks/defense/turrets/ContinuousTurret.java index e2cbcadffc..cfd5171b41 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/ContinuousTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/ContinuousTurret.java @@ -38,6 +38,12 @@ public class ContinuousTurret extends Turret{ public Seq bullets = new Seq<>(); public float lastLength = size * 4f; + @Override + public float estimateDps(){ + if(!hasAmmo()) return 0f; + return shootType.damage * 60f / (shootType instanceof ContinuousBulletType c ? c.damageInterval : 5f); + } + @Override protected void updateCooling(){ //TODO how does coolant work here, if at all? diff --git a/gradle.properties b/gradle.properties index d3e15e0aac..5c00967a82 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,4 +26,4 @@ org.gradle.caching=true org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 android.enableR8.fullMode=false -archash=2986baf556 +archash=1fdfcdc213 From 1f900b52f8761ca2ba4c0620044b9067f63c19ac Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 26 Jul 2024 23:44:37 -0400 Subject: [PATCH 347/348] Afflict building damage nerf --- core/src/mindustry/content/Blocks.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 5524503b49..dfd856d56a 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -4382,6 +4382,7 @@ public class Blocks{ trailInterval = 3f; trailParam = 4f; pierceCap = 2; + buildingDamageMultiplier = 0.5f; fragOnHit = false; speed = 5f; damage = 180f; From 57ddf4fc46c2d8a6f928f3b246904346db3bea99 Mon Sep 17 00:00:00 2001 From: summoner001 Date: Sun, 28 Jul 2024 15:51:08 +0200 Subject: [PATCH 348/348] Update bundle_hu.properties (#10065) Make the translation of the "launch" word coherent so that it has the same translation everywhere, because several translations have been used for it, which can confuse the player. Minor sentence fixes. --- core/assets/bundles/bundle_hu.properties | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index 3e473f5877..30fc904248 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -188,7 +188,7 @@ name = Név: noname = Előbb válassz egy[accent] nevet[]. search = Keresés: planetmap = Bolygótérkép -launchcore = Támaszpont indítása +launchcore = Támaszpont kilövése filename = Fájlnév: unlocked = Új tartalom feloldva! available = Új fejlesztés érhető el! @@ -672,7 +672,7 @@ requirement.produce = Gyártsd le: {0} requirement.capture = Foglald el a(z) {0} szektort requirement.onplanet = Szektor elfoglalása a(z) {0} bolygón requirement.onsector = Landolj a(z) {0} szektorban -launch.text = Indítás +launch.text = Kilövés research.multiplayer = Csak a kiszolgáló fedezhet fel nyersanyagokat. map.multiplayer = Csak a kiszolgáló tekintheti meg a szektorokat. uncover = Felfedés @@ -730,8 +730,8 @@ bannedunits = Tiltott egységek bannedunits.whitelist = Tiltott egységek fehérlistára bannedblocks.whitelist = Tiltott blokkok fehérlistára addall = Összes hozzáadása -launch.from = Indítás a(z) [accent]{0} szektorból -launch.capacity = Nyersanyag-kapacitás az indításkor: [accent]{0} +launch.from = Kilövés a(z) [accent]{0} szektorból +launch.capacity = Nyersanyag-kapacitás a kilövéskor: [accent]{0} launch.destination = Úticél: {0} configure.invalid = A mennyiségnek 0 és {0} között kell lennie. add = Hozzáadás... @@ -764,12 +764,12 @@ sectors.resources = Nyersanyagok: sectors.production = Termelés: sectors.export = Export: sectors.import = Import: -sectors.time = Idő: +sectors.time = Játékidő a szektorban: sectors.threat = Fenyegetés: sectors.wave = Hullám: sectors.stored = Tárolt nyersanyagok: sectors.resume = Folytatás -sectors.launch = Indítás +sectors.launch = Kilövés sectors.select = Kiválasztás sectors.nonelaunch = [lightgray]semmi (nap) sectors.rename = Szektor átnevezése @@ -778,7 +778,7 @@ sectors.vulnerable = [scarlet]Sebezhető sectors.underattack = [scarlet]Támadás alatt! [accent]{0}%-ban sérült sectors.underattack.nodamage = [scarlet]Nincs meghódítva sectors.survives = [accent]Túlél {0} hullámot -sectors.go = Utazás +sectors.go = Visszatérés sector.abandon = Elhagyás sector.abandon.confirm = Ennek a szektornak a támaszpontjai önmegsemmisítésre kerülnek.\nFolytatod? sector.curcapture = A szektor elfoglalva @@ -820,7 +820,7 @@ sector.fungalPass.name = Gombahágó sector.biomassFacility.name = Biomassza szintetizáló létesítmény sector.windsweptIslands.name = Szélfútta szigetek sector.extractionOutpost.name = Kivonási helyőrség -sector.planetaryTerminal.name = Bolygó körüli indítóterminál +sector.planetaryTerminal.name = Bolygó körüli kilövőállás sector.coastline.name = Partvonal sector.navalFortress.name = Tengerészeti erőd @@ -1138,7 +1138,7 @@ category.function = Funkció category.optional = Lehetséges fejlesztések setting.alwaysmusic.name = Folyamatos zenelejátszás setting.alwaysmusic.description = Ha engedélyezve van, akkor a zene folyamatosan szól a játékban.\nHa ki van kapcsolva, akkor csak véletlenszerű időközönként szólal meg. -setting.skipcoreanimation.name = Támaszpont indítási/leszállási animáció kihagyása +setting.skipcoreanimation.name = Támaszpont kilövés/leszállás animáció kihagyása setting.landscape.name = Fekvő mód zárolása setting.shadows.name = Árnyékok setting.blockreplace.name = Automatikus blokkjavaslatok @@ -1922,7 +1922,7 @@ hint.unitControl.mobile = [accent][[Dupla koppintással][] a szövetséges egys hint.unitSelectControl = Az egységek irányításához lépj be [accent]parancs módba[] a [accent]bal Shift[] lenyomva tartásával.\nParancs módban az egységek kijelöléséhez kattints, és húzd az egeret. A [accent]jobb egérgombbal[] küldd az egységeket a helyszínre vagy a célponthoz. hint.unitSelectControl.mobile = Az egységek irányításához lépj be [accent]parancs módba[] a bal alsó sarokban lévő [accent]parancs[] gombbal.\nParancs módban az egységek kiválasztásához érintsd meg a kijelzőt és húzással jelöld ki az egységeket. Koppintással küldd az egységeket a helyszínre vagy a célponthoz. hint.launch = Ha elegendő nyersanyagot gyűjtöttél össze, akkor [accent]lődd ki[] a támaszpontot a következő szektorba, úgy, hogy megnyitod a \ue827 [accent]Bolygótérképet[] a jobb alsó sarokban, és átforgatod az új helyszínre. -hint.launch.mobile = Ha elegendő nyersanyagot gyűjtöttél össze, akkor [accent]lődd ki[] el a támaszpontot egy közeli szektorba, úgy, hogy kiválasztasz egy szektort a \ue88c [accent]Menüben[] a \ue827 [accent]Bolygótérképről[]. +hint.launch.mobile = Ha elegendő nyersanyagot gyűjtöttél össze, akkor [accent]lődd ki[] a támaszpontot egy közeli szektorba, úgy, hogy kiválasztasz egy szektort a \ue88c [accent]Menüben[] a \ue827 [accent]Bolygótérképről[]. hint.schematicSelect = Tartsd nyomja az [accent][[F][] gombot több épület kijelöléséhez és másolásához.\n\n[accent][[Középső kattintással][] egy adott blokktípus másolható. hint.rebuildSelect = Tartsd nyomva a [accent][[B][] gombot és húzással jelöld ki a megsemmisített blokkterveket.\nEz automatikusan újraépíti őket. hint.rebuildSelect.mobile = Válaszd a \ue874 másolás gombot, majd koppints az \ue80f újraépítés gombra, és húzd a megsemmisült blokktervek kijelöléséhez.\nEz automatikusan újraépíti őket. @@ -1983,7 +1983,7 @@ onset.turretammo = Szállítótalagok használatával lásd el a lövegtornyokat onset.walls = A [accent]falak[] megakadályozhatják, hogy az épületekben károk keletkezzenek.\nÉpíts \uf6ee [accent]Berillium falakat[] a lövegtornyok körül. onset.enemies = Az ellenség közeledik, készülj fel a védekezésre. onset.defenses = [accent]Állíts fel védelmet:[lightgray] {0} -onset.attack = Az ellenség most sebezhető. Indítsd ellentámadást. +onset.attack = Az ellenség most sebezhető. Indíts ellentámadást. onset.cores = Új támaszpont csak a [accent]támaszpontcsempére[] helyezhető.\nAz új támaszpontok előretolt bázisként működnek, és megosztják a nyersanyagkészletüket más támaszpontokkal.\nHelyezz el egy \uf725 támaszpontot. onset.detect = Az ellenség 2 percen belül észrevesz téged.\nÁllíts fel védelmet, bányászatot és termelést. onset.commandmode = Tartsd nyomva a [accent]Shift[] gombot, hogy [accent]parancs módba[] lépj.\n[accent]Bal egérgombbal és húzással[] lehet egységeket kijelölni.\n[accent]Jobb egérgombbal[] az egységek mozgásra vagy támadásra utasíthatóak. @@ -2137,7 +2137,7 @@ block.cultivator.description = A légkörben szálló spórákat kapszulákba s block.cultivator.details = Visszaszerzett technológia. Hatalmas tömegű biomassza gyártására alkalmas a lehető leghatékonyabban. Valószínűleg a Serpulo felszínét ma borító spórák kezdeti inkubátora. block.oil-extractor.description = Nagy mennyiségű áramot, homokot és vizet használ, hogy olajat fúrjon. block.core-shard.description = Támaszpont. Ha elpusztul, a szektor elveszett. -block.core-shard.details = Az első modell. Kompakt. Önsokszorosító. Egyszer használatos indítórakétákkal van felszerelve, nem bolygóközi utazásra tervezték. +block.core-shard.details = Az első modell. Kompakt. Önsokszorosító. Egyszer használatos gyorsítórakétákkal van felszerelve, nem bolygóközi utazásra tervezték. block.core-foundation.description = Támaszpont. Jól páncélozott. Több nyersanyagot tárol, mint a Szilánk. block.core-foundation.details = A második modell. block.core-nucleus.description = Támaszpont. Rendkívül jól páncélozott. Hatalmas mennyiségű nyersanyag tárolására képes. @@ -2200,7 +2200,7 @@ block.titan.description = Hatalmas robbanóanyagú tüzérségi lövedéket lő block.afflict.description = Hatalmas töltésű repeszlövedék-gömböket lő ki. Hőt igényel. block.disperse.description = Égő repeszlövedékeket lő légi célpontokra. block.lustre.description = Lassan mozgó, egyszerre egy célpontra ható lézert lő az ellenséges célpontokra. -block.scathe.description = Nagy erejű rakétát indít jelentős távolságokra lévő földi célpontok ellen. +block.scathe.description = Nagy erejű rakétát lő ki jelentős távolságokra lévő földi célpontok ellen. block.smite.description = Átütő erejű, villámló lövedékeket lő ki. block.malign.description = Lézertöltetekből álló célzott sortüzet zúdít az ellenséges célpontokra. Jelentős fűtést igényel. block.silicon-arc-furnace.description = A homokot és a grafitot szilíciummá finomítja.