add missing translation, minor speedup generating map

This commit is contained in:
Anthony Calosa
2024-11-03 05:58:28 +08:00
parent bdcf8ecb87
commit c6aecafe6b
11 changed files with 545 additions and 428 deletions

View File

@@ -244,18 +244,21 @@ public class SaveLoadScene extends UIScene {
loaded = false;
if (WorldSave.load(currentSlot)) {
WorldSave.getCurrentSave().clearChanges();
WorldSave.getCurrentSave().getWorld().generateNew(0);
if (difficulty != null)
Current.player().updateDifficulty(Config.instance().getConfigData().difficulties[difficulty.getSelectedIndex()]);
Current.player().setWorldPosY((int) (WorldSave.getCurrentSave().getWorld().getData().playerStartPosY * WorldSave.getCurrentSave().getWorld().getData().height * WorldSave.getCurrentSave().getWorld().getTileSize()));
Current.player().setWorldPosX((int) (WorldSave.getCurrentSave().getWorld().getData().playerStartPosX * WorldSave.getCurrentSave().getWorld().getData().width * WorldSave.getCurrentSave().getWorld().getTileSize()));
Current.player().getQuests().clear();
Current.player().resetQuestFlags();
Current.player().setCharacterFlag("newGamePlus", 1);
AdventurePlayer.current().addQuest("28");
WorldStage.getInstance().enterSpawnPOI();
SoundSystem.instance.changeBackgroundTrack();
Forge.switchScene(GameScene.instance());
if (WorldSave.getCurrentSave().getWorld().generateNew(0)) {
if (difficulty != null)
Current.player().updateDifficulty(Config.instance().getConfigData().difficulties[difficulty.getSelectedIndex()]);
Current.player().setWorldPosY((int) (WorldSave.getCurrentSave().getWorld().getData().playerStartPosY * WorldSave.getCurrentSave().getWorld().getData().height * WorldSave.getCurrentSave().getWorld().getTileSize()));
Current.player().setWorldPosX((int) (WorldSave.getCurrentSave().getWorld().getData().playerStartPosX * WorldSave.getCurrentSave().getWorld().getData().width * WorldSave.getCurrentSave().getWorld().getTileSize()));
Current.player().getQuests().clear();
Current.player().resetQuestFlags();
Current.player().setCharacterFlag("newGamePlus", 1);
AdventurePlayer.current().addQuest("28");
WorldStage.getInstance().enterSpawnPOI();
SoundSystem.instance.changeBackgroundTrack();
Forge.switchScene(GameScene.instance());
} else {
Forge.clearTransitionScreen();
}
} else {
Forge.clearTransitionScreen();
}

View File

@@ -19,16 +19,23 @@ public class ColorMap
}
public ColorMap(FileHandle file) {
Pixmap pdata=new Pixmap(file);
width=pdata.getWidth();
height=pdata.getHeight();
data=new Color[width*height];
for(int y=0;y<height;y++)
for(int x=0;x<width;x++)
{
data[x+y*width]=new Color(pdata.getPixel(x,y));
}
pdata.dispose();
if (file.exists()) {
Pixmap pdata=new Pixmap(file);
width=pdata.getWidth();
height=pdata.getHeight();
data=new Color[width*height];
for(int y=0;y<height;y++)
for(int x=0;x<width;x++)
{
data[x+y*width]=new Color(pdata.getPixel(x,y));
}
pdata.dispose();
} else {
width = 0;
height = 0;
data = new Color[0];
System.err.println("Cannot find file for ColorMap: " + file.path());
}
}
public int getWidth() {

View File

@@ -26,6 +26,7 @@ import forge.gui.GuiBase;
import org.apache.commons.lang3.tuple.Pair;
import java.util.*;
import java.util.concurrent.CompletableFuture;
/**
* Class that will create the world from the configuration
@@ -289,502 +290,522 @@ public class World implements Disposable, SaveFileContent {
return currentTime;
}
public World generateNew(long seed) {
if (GuiBase.isAndroid())
GuiBase.getInterface().preventSystemSleep(true);
final long[] currentTime = {System.currentTimeMillis()};
long startTime = System.currentTimeMillis();
public boolean generateNew(long seed) {
try {
if (GuiBase.isAndroid())
GuiBase.getInterface().preventSystemSleep(true);
final long[] currentTime = {System.currentTimeMillis()};
long startTime = System.currentTimeMillis();
loadWorldData();
loadWorldData();
//////////////////
///////// initialize
//////////////////
if (seed == 0) {
seed = random.nextLong();
}
this.seed = seed;
random.setSeed(seed);
OpenSimplexNoise noise = new OpenSimplexNoise(seed);
float noiseZoom = data.noiseZoomBiome;
width = data.width;
height = data.height;
//save at all data
biomeMap = new long[width][height];
terrainMap = new int[width][height];
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
biomeMap[x][y] = 0;
terrainMap[x][y] = 0;
if (seed == 0) {
seed = random.nextLong();
}
}
this.seed = seed;
random.setSeed(seed);
OpenSimplexNoise noise = new OpenSimplexNoise(seed);
final int[] biomeIndex = {-1};
currentTime[0] = measureGenerationTime("loading data", currentTime[0]);
HashMap<BiomeStructureData, BiomeStructure> structureDataMap = new HashMap<>();
float noiseZoom = data.noiseZoomBiome;
width = data.width;
height = data.height;
//save at all data
biomeMap = new long[width][height];
terrainMap = new int[width][height];
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
biomeMap[x][y] = 0;
terrainMap[x][y] = 0;
}
}
final int[] biomeIndex = {-1};
currentTime[0] = measureGenerationTime("loading data", currentTime[0]);
HashMap<BiomeStructureData, BiomeStructure> structureDataMap = new HashMap<>();
//////////////////
///////// calculation structure position with wavefunctioncollapse
//////////////////
for (BiomeData biome : data.GetBiomes()) {
if (biome.structures != null) {
int biomeWidth = (int) Math.round(biome.width * (double) width);
int biomeHeight = (int) Math.round(biome.height * (double) height);
for (BiomeStructureData data : biome.structures) {
long localSeed = seed;
long threadStartTime = System.currentTimeMillis();
BiomeStructure structure = new BiomeStructure(data, localSeed, biomeWidth, biomeHeight);
structure.initialize();
structureDataMap.put(data, structure);
measureGenerationTime("wavefunctioncollapse " + data.sourcePath, threadStartTime);
List<CompletableFuture<Long>> futures = new ArrayList<>();
for (BiomeData biome : data.GetBiomes()) {
if (biome.structures != null) {
int biomeWidth = (int) Math.round(biome.width * (double) width);
int biomeHeight = (int) Math.round(biome.height * (double) height);
for (BiomeStructureData data : biome.structures) {
long localSeed = seed;
futures.add(CompletableFuture.supplyAsync(()-> {
long threadStartTime = System.currentTimeMillis();
BiomeStructure structure = new BiomeStructure(data, localSeed, biomeWidth, biomeHeight);
structure.initialize();
structureDataMap.put(data, structure);
return measureGenerationTime("wavefunctioncollapse " + data.sourcePath, threadStartTime);
}));
}
}
}
}
CompletableFuture<?>[] futuresArray = futures.toArray(new CompletableFuture<?>[0]);
CompletableFuture.allOf(futuresArray).join();
futures.clear();
//////////////////
///////// calculation each biome position based on noise and radius
//////////////////
for (BiomeData biome : data.GetBiomes()) {
for (BiomeData biome : data.GetBiomes()) {
biomeIndex[0]++;
int biomeXStart = (int) Math.round(biome.startPointX * (double) width);
int biomeYStart = (int) Math.round(biome.startPointY * (double) height);
int biomeWidth = (int) Math.round(biome.width * (double) width);
int biomeHeight = (int) Math.round(biome.height * (double) height);
biomeIndex[0]++;
int biomeXStart = (int) Math.round(biome.startPointX * (double) width);
int biomeYStart = (int) Math.round(biome.startPointY * (double) height);
int biomeWidth = (int) Math.round(biome.width * (double) width);
int biomeHeight = (int) Math.round(biome.height * (double) height);
int beginX = Math.max(biomeXStart - biomeWidth / 2, 0);
int beginY = Math.max(biomeYStart - biomeHeight / 2, 0);
int endX = Math.min(biomeXStart + biomeWidth / 2, width);
int endY = Math.min(biomeYStart + biomeHeight / 2, height);
if (biome.width == 1.0 && biome.height == 1.0) {
beginX = 0;
beginY = 0;
endX = width;
endY = height;
}
for (int x = beginX; x < endX; x++) {
for (int y = beginY; y < endY; y++) {
//value 0-1 based on noise
float noiseValue = ((float) noise.eval(x / (float) width * noiseZoom, y / (float) height * noiseZoom) + 1) / 2f;
noiseValue *= biome.noiseWeight;
//value 0-1 based on dist to origin
float distanceValue = ((float) Math.sqrt((x - biomeXStart) * (x - biomeXStart) + (y - biomeYStart) * (y - biomeYStart))) / (Math.max(biomeWidth, biomeHeight) / 2f);
distanceValue *= biome.distWeight;
if (noiseValue + distanceValue < 1.0 || biome.invertHeight && (1 - noiseValue) + distanceValue < 1.0) {
Color color = biome.GetColor();
float[] hsv = new float[3];
color.toHsv(hsv);
int count = (int) ((noiseValue - 0.5) * 10 / 4);
//hsv[2]+=(count*0.2);
biomeMap[x][y] |= (1L << biomeIndex[0]);
int terrainCounter = 1;
terrainMap[x][y] = 0;
if (biome.terrain != null) {
for (BiomeTerrainData terrain : biome.terrain) {
float terrainNoise = ((float) noise.eval(x / (float) width * (noiseZoom * terrain.resolution), y / (float) height * (noiseZoom * terrain.resolution)) + 1) / 2;
if (terrainNoise >= terrain.min && terrainNoise <= terrain.max) {
terrainMap[x][y] = terrainCounter;
//pix.fillRectangle(x*data.miniMapTileSize, y*data.miniMapTileSize,data.miniMapTileSize,data.miniMapTileSize);
}
terrainCounter++;
}
}
if (biome.collision)
terrainMap[x][y] |= collisionBit;
if (biome.structures != null) {
for (BiomeStructureData data : biome.structures) {
while (!structureDataMap.containsKey(data)) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
int beginX = Math.max(biomeXStart - biomeWidth / 2, 0);
int beginY = Math.max(biomeYStart - biomeHeight / 2, 0);
int endX = Math.min(biomeXStart + biomeWidth / 2, width);
int endY = Math.min(biomeYStart + biomeHeight / 2, height);
if (biome.width == 1.0 && biome.height == 1.0) {
beginX = 0;
beginY = 0;
endX = width;
endY = height;
}
for (int x = beginX; x < endX; x++) {
for (int y = beginY; y < endY; y++) {
//value 0-1 based on noise
float noiseValue = ((float) noise.eval(x / (float) width * noiseZoom, y / (float) height * noiseZoom) + 1) / 2f;
noiseValue *= biome.noiseWeight;
//value 0-1 based on dist to origin
float distanceValue = ((float) Math.sqrt((x - biomeXStart) * (x - biomeXStart) + (y - biomeYStart) * (y - biomeYStart))) / (Math.max(biomeWidth, biomeHeight) / 2f);
distanceValue *= biome.distWeight;
if (noiseValue + distanceValue < 1.0 || biome.invertHeight && (1 - noiseValue) + distanceValue < 1.0) {
Color color = biome.GetColor();
float[] hsv = new float[3];
color.toHsv(hsv);
int count = (int) ((noiseValue - 0.5) * 10 / 4);
//hsv[2]+=(count*0.2);
biomeMap[x][y] |= (1L << biomeIndex[0]);
int terrainCounter = 1;
terrainMap[x][y] = 0;
if (biome.terrain != null) {
for (BiomeTerrainData terrain : biome.terrain) {
float terrainNoise = ((float) noise.eval(x / (float) width * (noiseZoom * terrain.resolution), y / (float) height * (noiseZoom * terrain.resolution)) + 1) / 2;
if (terrainNoise >= terrain.min && terrainNoise <= terrain.max) {
terrainMap[x][y] = terrainCounter;
//pix.fillRectangle(x*data.miniMapTileSize, y*data.miniMapTileSize,data.miniMapTileSize,data.miniMapTileSize);
}
terrainCounter++;
}
}
if (biome.collision)
terrainMap[x][y] |= collisionBit;
if (biome.structures != null) {
for (BiomeStructureData data : biome.structures) {
while (!structureDataMap.containsKey(data)) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
BiomeStructure structure = structureDataMap.get(data);
int structureXStart = x - (biomeXStart - biomeWidth / 2) - (int) ((data.x * biomeWidth) - (data.width * biomeWidth / 2));
int structureYStart = y - (biomeYStart - biomeHeight / 2) - (int) ((data.y * biomeHeight) - (data.height * biomeHeight / 2));
BiomeStructure structure = structureDataMap.get(data);
int structureXStart = x - (biomeXStart - biomeWidth / 2) - (int) ((data.x * biomeWidth) - (data.width * biomeWidth / 2));
int structureYStart = y - (biomeYStart - biomeHeight / 2) - (int) ((data.y * biomeHeight) - (data.height * biomeHeight / 2));
int structureIndex = structure.objectID(structureXStart, structureYStart);
if (structureIndex >= 0) {
int structureIndex = structure.objectID(structureXStart, structureYStart);
if (structureIndex >= 0) {
terrainMap[x][y] = terrainCounter + structureIndex;
if (structure.collision(structureXStart, structureYStart))
terrainMap[x][y] |= collisionBit;
terrainMap[x][y] |= isStructureBit;
terrainMap[x][y] = terrainCounter + structureIndex;
if (structure.collision(structureXStart, structureYStart))
terrainMap[x][y] |= collisionBit;
terrainMap[x][y] |= isStructureBit;
}
terrainCounter += structure.structureObjectCount();
}
terrainCounter += structure.structureObjectCount();
}
}
}
}
}
}
}
currentTime[0] = measureGenerationTime("biomes in total", currentTime[0]);
currentTime[0] = measureGenerationTime("biomes in total", currentTime[0]);
//////////////////
///////// set poi placement
//////////////////
List<PointOfInterest> towns = new ArrayList<>();
List<PointOfInterest> notTowns = new ArrayList<>();
List<Rectangle> otherPoints = new ArrayList<>();
List<PointOfInterest> towns = new ArrayList<>();
List<PointOfInterest> notTowns = new ArrayList<>();
List<Rectangle> otherPoints = new ArrayList<>();
TextureAtlas mapMarker = Config.instance().getAtlas(Paths.MAP_MARKER);
TextureData texture = mapMarker.getTextures().first().getTextureData();
if (!texture.isPrepared())
texture.prepare();
Pixmap mapMarkerPixmap = texture.consumePixmap();
clearTerrain((int) (data.width * data.playerStartPosX), (int) (data.height * data.playerStartPosY), 10);
//otherPoints.add(new Rectangle(((float) data.width * data.playerStartPosX * (float) data.tileSize) - data.tileSize * 3, ((float) data.height * data.playerStartPosY * data.tileSize) - data.tileSize * 3, data.tileSize * 6, data.tileSize * 6));
boolean running = true;
here:
while (running) {
mapPoiIds = new PointOfInterestMap(getChunkSize(), data.tileSize, data.width / getChunkSize(), data.height / getChunkSize());
int biomeIndex2 = -1;
running = false;
for (BiomeData biome : data.GetBiomes()) {
biomeIndex2++;
for (PointOfInterestData poi : biome.getPointsOfInterest()) {
for (int i = 0; i < poi.count; i++) {
for (int counter = 0; counter < 500; counter++)//tries 500 times to find a free point
{
float radius = (float) Math.sqrt(((random.nextDouble()) / 2 * poi.radiusFactor));
float theta = (float) (random.nextDouble() * 2 * Math.PI);
float x = (float) (radius * Math.cos(theta));
x *= (biome.width * width / 2);
x += (biome.startPointX * width);
float y = (float) (radius * Math.sin(theta));
y *= (biome.height * height / 2);
y += (height - (biome.startPointY * height));
TextureAtlas mapMarker = Config.instance().getAtlas(Paths.MAP_MARKER);
TextureData texture = mapMarker.getTextures().first().getTextureData();
if (!texture.isPrepared())
texture.prepare();
Pixmap mapMarkerPixmap = texture.consumePixmap();
clearTerrain((int) (data.width * data.playerStartPosX), (int) (data.height * data.playerStartPosY), 10);
//otherPoints.add(new Rectangle(((float) data.width * data.playerStartPosX * (float) data.tileSize) - data.tileSize * 3, ((float) data.height * data.playerStartPosY * data.tileSize) - data.tileSize * 3, data.tileSize * 6, data.tileSize * 6));
boolean running = true;
here:
while (running) {
mapPoiIds = new PointOfInterestMap(getChunkSize(), data.tileSize, data.width / getChunkSize(), data.height / getChunkSize());
int biomeIndex2 = -1;
running = false;
for (BiomeData biome : data.GetBiomes()) {
biomeIndex2++;
for (PointOfInterestData poi : biome.getPointsOfInterest()) {
for (int i = 0; i < poi.count; i++) {
for (int counter = 0; counter < 500; counter++)//tries 500 times to find a free point
{
float radius = (float) Math.sqrt(((random.nextDouble()) / 2 * poi.radiusFactor));
float theta = (float) (random.nextDouble() * 2 * Math.PI);
float x = (float) (radius * Math.cos(theta));
x *= (biome.width * width / 2);
x += (biome.startPointX * width);
float y = (float) (radius * Math.sin(theta));
y *= (biome.height * height / 2);
y += (height - (biome.startPointY * height));
y += (poi.offsetY * (biome.height * height));
x += (poi.offsetX * (biome.width * width));
y += (poi.offsetY * (biome.height * height));
x += (poi.offsetX * (biome.width * width));
if ((int) x < 0 || (int) y <= 0 || (int) y >= height || (int) x >= width || biomeIndex2 != highestBiome(getBiome((int) x, (int) y))) {
continue;
}
x *= data.tileSize;
y *= data.tileSize;
boolean breakNextLoop = false;
for (Rectangle rect : otherPoints) {
if (rect.contains(x, y)) {
breakNextLoop = true;
break;
}
}
if (breakNextLoop) {
boolean foundSolution = false;
boolean noSolution = false;
breakNextLoop = false;
for (int xi = -1; xi < 2 && !foundSolution; xi++) {
for (int yi = -1; yi < 2 && !foundSolution; yi++) {
for (Rectangle rect : otherPoints) {
if (rect.contains(x + xi * data.tileSize, y + yi * data.tileSize)) {
noSolution = true;
break;
}
}
if (!noSolution) {
foundSolution = true;
x = x + xi * data.tileSize;
y = y + yi * data.tileSize;
}
}
}
if (!foundSolution) {
if (counter == 499) {
System.err.print("Can not place POI " + poi.name + "...Rerunning..\n");
running = true;
towns.clear();
notTowns.clear();
otherPoints.clear();
clearTerrain((int) (data.width * data.playerStartPosX), (int) (data.height * data.playerStartPosY), 10);
storedInfo.clear();
continue here;
}
if ((int) x < 0 || (int) y <= 0 || (int) y >= height || (int) x >= width || biomeIndex2 != highestBiome(getBiome((int) x, (int) y))) {
continue;
}
}
otherPoints.add(new Rectangle(x - data.tileSize * 4, y - data.tileSize * 4, data.tileSize * 8, data.tileSize * 8));
PointOfInterest newPoint = new PointOfInterest(poi, new Vector2(x, y), random);
clearTerrain((int) (x / data.tileSize), (int) (y / data.tileSize), 3);
mapPoiIds.add(newPoint);
TextureAtlas.AtlasRegion marker = mapMarker.findRegion(poi.type);
x *= data.tileSize;
y *= data.tileSize;
if (marker != null) {
int xInPixels = (int) ((x / data.tileSize) * data.miniMapTileSize);
int yInPixels = (int) ((height - (y / data.tileSize)) * data.miniMapTileSize);
xInPixels -= (marker.getRegionWidth() / 2);
yInPixels -= (marker.getRegionHeight() / 2);
drawPixmapLater(mapMarkerPixmap, marker.getRegionX(), marker.getRegionY(),
marker.getRegionWidth(), marker.getRegionHeight(), xInPixels, yInPixels, marker.getRegionWidth(), marker.getRegionHeight());
}
if (poi.type != null && (poi.type.equals("town") || poi.type.equals("capital"))) {
if (!newPoint.hasDisplayName()) {
if (poi.displayName == null || poi.displayName.isEmpty()) {
newPoint.setDisplayName(biome.getNewTownName());
} else {
newPoint.setDisplayName(poi.getDisplayName());
boolean breakNextLoop = false;
for (Rectangle rect : otherPoints) {
if (rect.contains(x, y)) {
breakNextLoop = true;
break;
}
}
towns.add(newPoint);
} else {
notTowns.add(newPoint);
if (breakNextLoop) {
boolean foundSolution = false;
boolean noSolution = false;
breakNextLoop = false;
for (int xi = -1; xi < 2 && !foundSolution; xi++) {
for (int yi = -1; yi < 2 && !foundSolution; yi++) {
for (Rectangle rect : otherPoints) {
if (rect.contains(x + xi * data.tileSize, y + yi * data.tileSize)) {
noSolution = true;
break;
}
}
if (!noSolution) {
foundSolution = true;
x = x + xi * data.tileSize;
y = y + yi * data.tileSize;
}
}
}
if (!foundSolution) {
if (counter == 499) {
System.err.print("Can not place POI " + poi.name + "...Rerunning..\n");
running = true;
towns.clear();
notTowns.clear();
otherPoints.clear();
clearTerrain((int) (data.width * data.playerStartPosX), (int) (data.height * data.playerStartPosY), 10);
storedInfo.clear();
continue here;
}
continue;
}
}
otherPoints.add(new Rectangle(x - data.tileSize * 4, y - data.tileSize * 4, data.tileSize * 8, data.tileSize * 8));
PointOfInterest newPoint = new PointOfInterest(poi, new Vector2(x, y), random);
clearTerrain((int) (x / data.tileSize), (int) (y / data.tileSize), 3);
mapPoiIds.add(newPoint);
TextureAtlas.AtlasRegion marker = mapMarker.findRegion(poi.type);
if (marker != null) {
int xInPixels = (int) ((x / data.tileSize) * data.miniMapTileSize);
int yInPixels = (int) ((height - (y / data.tileSize)) * data.miniMapTileSize);
xInPixels -= (marker.getRegionWidth() / 2);
yInPixels -= (marker.getRegionHeight() / 2);
drawPixmapLater(mapMarkerPixmap, marker.getRegionX(), marker.getRegionY(),
marker.getRegionWidth(), marker.getRegionHeight(), xInPixels, yInPixels, marker.getRegionWidth(), marker.getRegionHeight());
}
if (poi.type != null && (poi.type.equals("town") || poi.type.equals("capital"))) {
if (!newPoint.hasDisplayName()) {
if (poi.displayName == null || poi.displayName.isEmpty()) {
newPoint.setDisplayName(biome.getNewTownName());
} else {
newPoint.setDisplayName(poi.getDisplayName());
}
}
towns.add(newPoint);
} else {
notTowns.add(newPoint);
}
break;
}
break;
}
}
}
}
}
currentTime[0] = measureGenerationTime("poi placement", currentTime[0]);
currentTime[0] = measureGenerationTime("poi placement", currentTime[0]);
//////////////////
///////// sort towns and build roads in between
//////////////////
List<Pair<PointOfInterest, PointOfInterest>> allSortedTowns = new ArrayList<>();
List<Pair<PointOfInterest, PointOfInterest>> allSortedTowns = new ArrayList<>();
HashSet<Long> usedEdges = new HashSet<>();//edge is first 32 bits id of first id and last 32 bits id of second
for (int i = 0; i < towns.size() - 1; i++) {
HashSet<Long> usedEdges = new HashSet<>();//edge is first 32 bits id of first id and last 32 bits id of second
for (int i = 0; i < towns.size() - 1; i++) {
PointOfInterest current = towns.get(i);
int smallestIndex = -1;
int secondSmallestIndex = -1;
float smallestDistance = Float.MAX_VALUE;
for (int j = 0; j < towns.size(); j++) {
PointOfInterest current = towns.get(i);
int smallestIndex = -1;
int secondSmallestIndex = -1;
float smallestDistance = Float.MAX_VALUE;
for (int j = 0; j < towns.size(); j++) {
if (i == j || usedEdges.contains((long) i | ((long) j << 32)))
if (i == j || usedEdges.contains((long) i | ((long) j << 32)))
continue;
float dist = current.getPosition().dst(towns.get(j).getPosition());
if (dist > data.maxRoadDistance)
continue;
if (dist < smallestDistance) {
smallestDistance = dist;
secondSmallestIndex = smallestIndex;
smallestIndex = j;
}
}
if (smallestIndex < 0)
continue;
float dist = current.getPosition().dst(towns.get(j).getPosition());
if (dist > data.maxRoadDistance)
usedEdges.add((long) i | ((long) smallestIndex << 32));
usedEdges.add((long) i << 32 | ((long) smallestIndex));
allSortedTowns.add(Pair.of(current, towns.get(smallestIndex)));
if (secondSmallestIndex < 0)
continue;
if (dist < smallestDistance) {
smallestDistance = dist;
secondSmallestIndex = smallestIndex;
smallestIndex = j;
}
usedEdges.add((long) i | ((long) secondSmallestIndex << 32));
usedEdges.add((long) i << 32 | ((long) secondSmallestIndex));
//allSortedTowns.add(Pair.of(current, towns.get(secondSmallestIndex)));
}
if (smallestIndex < 0)
continue;
usedEdges.add((long) i | ((long) smallestIndex << 32));
usedEdges.add((long) i << 32 | ((long) smallestIndex));
allSortedTowns.add(Pair.of(current, towns.get(smallestIndex)));
List<Pair<PointOfInterest, PointOfInterest>> allPOIPathsToNextTown = new ArrayList<>();
for (int i = 0; i < notTowns.size() - 1; i++) {
if (secondSmallestIndex < 0)
continue;
usedEdges.add((long) i | ((long) secondSmallestIndex << 32));
usedEdges.add((long) i << 32 | ((long) secondSmallestIndex));
//allSortedTowns.add(Pair.of(current, towns.get(secondSmallestIndex)));
}
List<Pair<PointOfInterest, PointOfInterest>> allPOIPathsToNextTown = new ArrayList<>();
for (int i = 0; i < notTowns.size() - 1; i++) {
PointOfInterest poi = notTowns.get(i);
int smallestIndex = -1;
float smallestDistance = Float.MAX_VALUE;
for (int j = 0; j < towns.size(); j++) {
PointOfInterest poi = notTowns.get(i);
int smallestIndex = -1;
float smallestDistance = Float.MAX_VALUE;
for (int j = 0; j < towns.size(); j++) {
float dist = poi.getPosition().dst(towns.get(j).getPosition());
if (dist < smallestDistance) {
smallestDistance = dist;
smallestIndex = j;
float dist = poi.getPosition().dst(towns.get(j).getPosition());
if (dist < smallestDistance) {
smallestDistance = dist;
smallestIndex = j;
}
}
if (smallestIndex < 0)
continue;
allPOIPathsToNextTown.add(Pair.of(poi, towns.get(smallestIndex)));
}
if (smallestIndex < 0)
continue;
allPOIPathsToNextTown.add(Pair.of(poi, towns.get(smallestIndex)));
}
biomeIndex[0]++;
biomeIndex[0]++;
//reset terrain path to the next town
for (Pair<PointOfInterest, PointOfInterest> poiToTown : allPOIPathsToNextTown) {
//reset terrain path to the next town
for (Pair<PointOfInterest, PointOfInterest> poiToTown : allPOIPathsToNextTown) {
futures.add(CompletableFuture.supplyAsync(()-> {
int startX = (int) poiToTown.getKey().getTilePosition(data.tileSize).x;
int startY = (int) poiToTown.getKey().getTilePosition(data.tileSize).y;
int x1 = (int) poiToTown.getValue().getTilePosition(data.tileSize).x;
int y1 = (int) poiToTown.getValue().getTilePosition(data.tileSize).y;
int dx = Math.abs(x1 - startX);
int dy = Math.abs(y1 - startY);
int sx = startX < x1 ? 1 : -1;
int sy = startY < y1 ? 1 : -1;
int err = dx - dy;
int e2;
for (int i = 0; i < 1000; i++) {
if (startX < 0 || startY <= 0 || startX >= width || startY > height) continue;
if ((terrainMap[startX][height - startY] & collisionBit) != 0)//clear terrain if it has collision
terrainMap[startX][height - startY] = 0;
int startX = (int) poiToTown.getKey().getTilePosition(data.tileSize).x;
int startY = (int) poiToTown.getKey().getTilePosition(data.tileSize).y;
int x1 = (int) poiToTown.getValue().getTilePosition(data.tileSize).x;
int y1 = (int) poiToTown.getValue().getTilePosition(data.tileSize).y;
int dx = Math.abs(x1 - startX);
int dy = Math.abs(y1 - startY);
int sx = startX < x1 ? 1 : -1;
int sy = startY < y1 ? 1 : -1;
int err = dx - dy;
int e2;
for (int i = 0; i < 1000; i++) {
if (startX < 0 || startY <= 0 || startX >= width || startY > height) continue;
if ((terrainMap[startX][height - startY] & collisionBit) != 0)//clear terrain if it has collision
terrainMap[startX][height - startY] = 0;
if (startX == x1 && startY == y1)
break;
e2 = 2 * err;
if (e2 > -dy) {
err = err - dy;
startX = startX + sx;
} else if (e2 < dx) {
err = err + dx;
startY = startY + sy;
}
if (startX == x1 && startY == y1)
break;
e2 = 2 * err;
if (e2 > -dy) {
err = err - dy;
startX = startX + sx;
} else if (e2 < dx) {
err = err + dx;
startY = startY + sy;
}
}
return 0l;
}));
}
}
futuresArray = futures.toArray(new CompletableFuture<?>[0]);
CompletableFuture.allOf(futuresArray).join();
futures.clear();
for (Pair<PointOfInterest, PointOfInterest> townPair : allSortedTowns) {
futures.add(CompletableFuture.supplyAsync(()-> {
int startX = (int) townPair.getKey().getTilePosition(data.tileSize).x;
int startY = (int) townPair.getKey().getTilePosition(data.tileSize).y;
int x1 = (int) townPair.getValue().getTilePosition(data.tileSize).x;
int y1 = (int) townPair.getValue().getTilePosition(data.tileSize).y;
for (int x = startX - 1; x < startX + 2; x++) {
for (int y = startY - 1; y < startY + 2; y++) {
if (x < 0 || y < 0 || x >= width || y >= height) continue;
biomeMap[x][height - y - 1] |= (1L << biomeIndex[0]);
terrainMap[x][height - y - 1] = 0;
}
}
int dx = Math.abs(x1 - startX);
int dy = Math.abs(y1 - startY);
int sx = startX < x1 ? 1 : -1;
int sy = startY < y1 ? 1 : -1;
int err = dx - dy;
int e2;
for (int i = 0; i < 1000; i++) {
if (startX < 0 || startY <= 0 || startX >= width || startY > height) continue;
biomeMap[startX][height - startY] |= (1L << biomeIndex[0]);
terrainMap[startX][height - startY] = 0;
for (Pair<PointOfInterest, PointOfInterest> townPair : allSortedTowns) {
int startX = (int) townPair.getKey().getTilePosition(data.tileSize).x;
int startY = (int) townPair.getKey().getTilePosition(data.tileSize).y;
int x1 = (int) townPair.getValue().getTilePosition(data.tileSize).x;
int y1 = (int) townPair.getValue().getTilePosition(data.tileSize).y;
for (int x = startX - 1; x < startX + 2; x++) {
for (int y = startY - 1; y < startY + 2; y++) {
if (x < 0 || y < 0 || x >= width || y >= height) continue;
biomeMap[x][height - y - 1] |= (1L << biomeIndex[0]);
terrainMap[x][height - y - 1] = 0;
}
if (startX == x1 && startY == y1)
break;
e2 = 2 * err;
if (e2 > -dy) {
err = err - dy;
startX = startX + sx;
} else if (e2 < dx) {
err = err + dx;
startY = startY + sy;
}
}
return 0l;
}));
}
int dx = Math.abs(x1 - startX);
int dy = Math.abs(y1 - startY);
int sx = startX < x1 ? 1 : -1;
int sy = startY < y1 ? 1 : -1;
int err = dx - dy;
int e2;
for (int i = 0; i < 1000; i++) {
if (startX < 0 || startY <= 0 || startX >= width || startY > height) continue;
biomeMap[startX][height - startY] |= (1L << biomeIndex[0]);
terrainMap[startX][height - startY] = 0;
if (startX == x1 && startY == y1)
break;
e2 = 2 * err;
if (e2 > -dy) {
err = err - dy;
startX = startX + sx;
} else if (e2 < dx) {
err = err + dx;
startY = startY + sy;
}
}
}
currentTime[0] = measureGenerationTime("roads", currentTime[0]);
futuresArray = futures.toArray(new CompletableFuture<?>[0]);
CompletableFuture.allOf(futuresArray).join();
futures.clear();
currentTime[0] = measureGenerationTime("roads", currentTime[0]);
//////////////////
///////// draw mini map
//////////////////
Pixmap pix = new Pixmap(width * data.miniMapTileSize, height * data.miniMapTileSize, Pixmap.Format.RGBA8888);
pix.setColor(1, 0, 0, 1);
pix.fill();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (highestBiome(biomeMap[x][y]) >= data.GetBiomes().size()) {
Pixmap smallPixmap = createSmallPixmap(data.roadTileset.tilesetAtlas, data.roadTileset.tilesetName, 0);
pix.drawPixmap(smallPixmap, x * data.miniMapTileSize, y * data.miniMapTileSize);
} else {
BiomeData biome = data.GetBiomes().get(highestBiome(biomeMap[x][y]));
int terrainIndex = terrainMap[x][y] & ~terrainMask;
if (terrainIndex > biome.terrain.length) {
Pixmap smallPixmap = createSmallPixmap(biome.tilesetAtlas, biome.tilesetName, 0);
Pixmap pix = new Pixmap(width * data.miniMapTileSize, height * data.miniMapTileSize, Pixmap.Format.RGBA8888);
pix.setColor(1, 0, 0, 1);
pix.fill();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (highestBiome(biomeMap[x][y]) >= data.GetBiomes().size()) {
Pixmap smallPixmap = createSmallPixmap(data.roadTileset.tilesetAtlas, data.roadTileset.tilesetName, 0);
pix.drawPixmap(smallPixmap, x * data.miniMapTileSize, y * data.miniMapTileSize);
terrainIndex -= biome.terrain.length;
terrainIndex--;
for (BiomeStructureData structData : biome.structures) {
if (terrainIndex >= structData.mappingInfo.length) {
terrainIndex -= structData.mappingInfo.length;
continue;
}
smallPixmap = createSmallPixmap(structData.structureAtlasPath, structData.mappingInfo[terrainIndex].name, 0);
pix.drawPixmap(smallPixmap, x * data.miniMapTileSize, y * data.miniMapTileSize);
break;
}
} else {
Pixmap smallPixmap = createSmallPixmap(biome.tilesetAtlas, biome.tilesetName, terrainIndex);
pix.drawPixmap(smallPixmap, x * data.miniMapTileSize, y * data.miniMapTileSize);
BiomeData biome = data.GetBiomes().get(highestBiome(biomeMap[x][y]));
int terrainIndex = terrainMap[x][y] & ~terrainMask;
if (terrainIndex > biome.terrain.length) {
Pixmap smallPixmap = createSmallPixmap(biome.tilesetAtlas, biome.tilesetName, 0);
pix.drawPixmap(smallPixmap, x * data.miniMapTileSize, y * data.miniMapTileSize);
terrainIndex -= biome.terrain.length;
terrainIndex--;
for (BiomeStructureData structData : biome.structures) {
if (terrainIndex >= structData.mappingInfo.length) {
terrainIndex -= structData.mappingInfo.length;
continue;
}
smallPixmap = createSmallPixmap(structData.structureAtlasPath, structData.mappingInfo[terrainIndex].name, 0);
pix.drawPixmap(smallPixmap, x * data.miniMapTileSize, y * data.miniMapTileSize);
break;
}
} else {
Pixmap smallPixmap = createSmallPixmap(biome.tilesetAtlas, biome.tilesetName, terrainIndex);
pix.drawPixmap(smallPixmap, x * data.miniMapTileSize, y * data.miniMapTileSize);
}
}
}
}
}
for (Map.Entry<String, Pair<Pixmap, HashMap<String, Pixmap>>> entry : pixmapHash.entrySet()) {
try {
entry.getValue().getLeft().dispose();
} catch (Exception e) {
//e.printStackTrace();
}
for (Map.Entry<String, Pixmap> pairEntry : entry.getValue().getRight().entrySet()) {
for (Map.Entry<String, Pair<Pixmap, HashMap<String, Pixmap>>> entry : pixmapHash.entrySet()) {
try {
pairEntry.getValue().dispose();
entry.getValue().getLeft().dispose();
} catch (Exception e) {
//e.printStackTrace();
}
for (Map.Entry<String, Pixmap> pairEntry : entry.getValue().getRight().entrySet()) {
try {
pairEntry.getValue().dispose();
} catch (Exception e) {
//e.printStackTrace();
}
}
}
}
pixmapHash.clear();
try {
drawPixmapNow(pix);
} catch (Exception e) {
//e.printStackTrace();
}
currentTime[0] = measureGenerationTime("mini map", currentTime[0]);
pixmapHash.clear();
try {
drawPixmapNow(pix);
} catch (Exception e) {
//e.printStackTrace();
}
currentTime[0] = measureGenerationTime("mini map", currentTime[0]);
//////////////////
///////// distribute small rocks and trees across the map
//////////////////
mapObjectIds = new SpritesDataMap(getChunkSize(), data.tileSize, data.width / getChunkSize());
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int invertedHeight = height - y - 1;
int currentBiome = highestBiome(biomeMap[x][invertedHeight]);
if (currentBiome >= data.GetBiomes().size())
continue;//roads
if (isStructure(x, y))
continue;
BiomeData biome = data.GetBiomes().get(currentBiome);
for (String name : biome.spriteNames) {
BiomeSpriteData sprite = data.GetBiomeSprites().getSpriteData(name);
double spriteNoise = (noise.eval(x / (double) width * noiseZoom * sprite.resolution, y / (double) invertedHeight * noiseZoom * sprite.resolution) + 1) / 2;
if (spriteNoise >= sprite.startArea && spriteNoise <= sprite.endArea) {
if (random.nextFloat() <= sprite.density) {
String spriteKey = sprite.key();
int key;
if (!mapObjectIds.containsKey(spriteKey)) {
mapObjectIds = new SpritesDataMap(getChunkSize(), data.tileSize, data.width / getChunkSize());
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int invertedHeight = height - y - 1;
int currentBiome = highestBiome(biomeMap[x][invertedHeight]);
if (currentBiome >= data.GetBiomes().size())
continue;//roads
if (isStructure(x, y))
continue;
BiomeData biome = data.GetBiomes().get(currentBiome);
for (String name : biome.spriteNames) {
BiomeSpriteData sprite = data.GetBiomeSprites().getSpriteData(name);
double spriteNoise = (noise.eval(x / (double) width * noiseZoom * sprite.resolution, y / (double) invertedHeight * noiseZoom * sprite.resolution) + 1) / 2;
if (spriteNoise >= sprite.startArea && spriteNoise <= sprite.endArea) {
if (random.nextFloat() <= sprite.density) {
String spriteKey = sprite.key();
int key;
if (!mapObjectIds.containsKey(spriteKey)) {
key = mapObjectIds.put(sprite.key(), sprite, data.GetBiomeSprites());
} else {
key = mapObjectIds.intKey(spriteKey);
key = mapObjectIds.put(sprite.key(), sprite, data.GetBiomeSprites());
} else {
key = mapObjectIds.intKey(spriteKey);
}
mapObjectIds.putPosition(key, new Vector2((((float) x) + .25f + random.nextFloat() / 2) * data.tileSize, (((float) y + .25f) - random.nextFloat() / 2) * data.tileSize));
break;//only on sprite per point
}
mapObjectIds.putPosition(key, new Vector2((((float) x) + .25f + random.nextFloat() / 2) * data.tileSize, (((float) y + .25f) - random.nextFloat() / 2) * data.tileSize));
break;//only on sprite per point
}
}
}
}
}
mapMarkerPixmap.dispose();
biomeImage = pix;
measureGenerationTime("sprites", currentTime[0]);
System.out.println("Generating world took :\t\t" + ((System.currentTimeMillis() - startTime) / 1000f) + " s");
WorldStage.getInstance().clearCache();
mapMarkerPixmap.dispose();
biomeImage = pix;
measureGenerationTime("sprites", currentTime[0]);
System.out.println("Generating world took :\t\t" + ((System.currentTimeMillis() - startTime) / 1000f) + " s");
WorldStage.getInstance().clearCache();
if (GuiBase.isAndroid())
GuiBase.getInterface().preventSystemSleep(false);
return this;
if (GuiBase.isAndroid())
GuiBase.getInterface().preventSystemSleep(false);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
HashMap<String, Pair<Pixmap, HashMap<String, Pixmap>>> pixmapHash = new HashMap<>();

View File

@@ -79,7 +79,8 @@ public class WorldSave {
} catch (Exception e) {
System.err.println("Generating New World");
currentSave.world.generateNew(0);
if (!currentSave.world.generateNew(0))
return false;
}
currentSave.onLoadList.emit();

View File

@@ -3444,4 +3444,17 @@ lblRelease=Freigeben
lblSnapshot=Schnappschuss
lblNewSnapshotVersion=NEU FORGE-{0}!
cbSnapshotUpdate=Überprüfen Sie Snapshot-Updates beim Start.
nlSnapshotUpdate=Wenn diese Option aktiviert ist, werden Snapshot-Updates beim Start automatisch überprüft und die Benachrichtigung in der Titelleiste angezeigt.
nlSnapshotUpdate=Wenn diese Option aktiviert ist, werden Snapshot-Updates beim Start automatisch überprüft und die Benachrichtigung in der Titelleiste angezeigt.
lblAddCommander=Satz
lblaspartnercommander=als Partnerkommandant
lblfromattractiondeck=vom Attraktionsdeck
lbltoattractiondeck=zum Attraktionsdeck
lblAddDeckSection=Variantenabschnitt hinzufügen...
lblAddDeckSectionSelect=Wählen Sie den Abschnitt zum Hinzufügen aus
lblFrom=aus
lblAttractions=Attraktionen
lblConspiracies=Verschwörungen
lblChooseOrderCardsPutIntoAttractionDeck=Wählen Sie die Reihenfolge der Karten aus, die in den Attraktionsstapel gelegt werden sollen
lblAttractionRollResult={0} gewürfelt, um ihre Attraktionen zu besuchen. Ergebnis: {1}.
lblAttractionDeckZone=Attraktionsdeck
lblLights=Lichter

View File

@@ -3458,4 +3458,11 @@ lblRelease=Liberar
lblSnapshot=Instantánea
lblNewSnapshotVersion=NUEVO FORGE-{0}!
cbSnapshotUpdate=Verifique las actualizaciones de instantáneas al inicio.
nlSnapshotUpdate=Cuando está habilitado, verifica automáticamente las actualizaciones de instantáneas al inicio y muestra la notificación en la barra de título.
nlSnapshotUpdate=Cuando está habilitado, verifica automáticamente las actualizaciones de instantáneas al inicio y muestra la notificación en la barra de título.
lblaspartnercommander=como comandante compañero
lblfromattractiondeck=desde la plataforma de atracción
lbltoattractiondeck=a la plataforma de atracción
lblFrom=de
lblChooseOrderCardsPutIntoAttractionDeck=Elige el orden de las cartas para colocarlas en el mazo de atracciones.
lblAttractionRollResult={0} rodó para visitar sus atracciones. Resultado: {1}.
lblLights=Luces

View File

@@ -3452,4 +3452,17 @@ lblRelease=Libérer
lblSnapshot=Instantané
lblNewSnapshotVersion=NOUVEAU FORGE{0}!
cbSnapshotUpdate=Vérifiez les mises à jour des instantanés au démarrage.
nlSnapshotUpdate=Lorsqu'il est activé, vérifie automatiquement les mises à jour des instantanés au démarrage et affiche la notification sur la barre de titre.
nlSnapshotUpdate=Lorsqu'il est activé, vérifie automatiquement les mises à jour des instantanés au démarrage et affiche la notification sur la barre de titre.
lblAddCommander=Ensemble
lblaspartnercommander=en tant que commandant partenaire
lblfromattractiondeck=depuis le pont d'attraction
lbltoattractiondeck=au pont d'attraction
lblAddDeckSection=Ajouter une section de variante...
lblAddDeckSectionSelect=Sélectionnez la section à ajouter
lblFrom=depuis
lblAttractions=Attractions
lblConspiracies=Conspirations
lblChooseOrderCardsPutIntoAttractionDeck=Choisissez l'ordre des cartes à mettre dans le jeu d'attraction
lblAttractionRollResult={0} ont lancé un jet pour visiter leurs attractions. Résultat: {1}.
lblAttractionDeckZone=pont d'attraction
lblLights=Lumières

View File

@@ -3450,4 +3450,17 @@ lblRelease=Pubblicazione
lblSnapshot=Istantanea
lblNewSnapshotVersion=NUOVO FORGE-{0}!
cbSnapshotUpdate=Controlla gli aggiornamenti delle istantanee all'avvio.
nlSnapshotUpdate=Se abilitato, controlla automaticamente gli aggiornamenti delle istantanee all'avvio e visualizza la notifica sulla barra del titolo.
nlSnapshotUpdate=Se abilitato, controlla automaticamente gli aggiornamenti delle istantanee all'avvio e visualizza la notifica sulla barra del titolo.
lblAddCommander=Impostato
lblaspartnercommander=come comandante partner
lblfromattractiondeck=dal ponte delle attrazioni
lbltoattractiondeck=al ponte delle attrazioni
lblAddDeckSection=Aggiungi sezione variante...
lblAddDeckSectionSelect=Seleziona la sezione da aggiungere
lblFrom=da
lblAttractions=Attrazioni
lblConspiracies=Cospirazioni
lblChooseOrderCardsPutIntoAttractionDeck=Scegli l'ordine delle carte da inserire nel mazzo delle attrazioni
lblAttractionRollResult={0} ha rotolato per visitare le sue attrazioni. Risultato: {1}.
lblAttractionDeckZone=attrazione
lblLights=Luci

View File

@@ -3446,4 +3446,17 @@ lblRelease=リリース
lblSnapshot=スナップショット
lblNewSnapshotVersion=新しい FORGE-{0}!
cbSnapshotUpdate=起動時にスナップショットの更新を確認します。
nlSnapshotUpdate=有効にすると、起動時にスナップショットの更新が自動的にチェックされ、タイトル バーに通知が表示されます。
nlSnapshotUpdate=有効にすると、起動時にスナップショットの更新が自動的にチェックされ、タイトル バーに通知が表示されます。
lblAddCommander=セット
lblaspartnercommander=パートナー指揮官として
lblfromattractiondeck=アトラクションデッキから
lbltoattractiondeck=アトラクションデッキへ
lblAddDeckSection=バリアント セクションを追加...
lblAddDeckSectionSelect=追加するセクションを選択してください
lblFrom=から
lblAttractions=アトラクション
lblConspiracies=陰謀
lblChooseOrderCardsPutIntoAttractionDeck=アトラクションデッキに入れるカードの順番を選択してください
lblAttractionRollResult={0} はアトラクションを訪れるために転がりました。結果: {1}。
lblAttractionDeckZone=アトラクションデッキ
lblLights=ライト

View File

@@ -3536,4 +3536,17 @@ lblRelease=Liberar
lblSnapshot=Instantâneo
lblNewSnapshotVersion=NOVO FORGE-{0}!
cbSnapshotUpdate=Verifique as atualizações de instantâneos na inicialização.
nlSnapshotUpdate=Quando ativado, verifica automaticamente as atualizações do snapshot na inicialização e exibe a notificação na barra de título.
nlSnapshotUpdate=Quando ativado, verifica automaticamente as atualizações do snapshot na inicialização e exibe a notificação na barra de título.
lblAddCommander=Definir
lblaspartnercommander=como comandante parceiro
lblfromattractiondeck=do deck de atração
lbltoattractiondeck=para o deck de atração
lblAddDeckSection=Adicionar seção de variante...
lblAddDeckSectionSelect=Selecione a seção a ser adicionada
lblFrom=de
lblAttractions=Atrações
lblConspiracies=Conspirações
lblChooseOrderCardsPutIntoAttractionDeck=Escolha a ordem das cartas para colocar no baralho de atração
lblAttractionRollResult={0} rolou para visitar suas atrações. Resultado: {1}.
lblAttractionDeckZone=deck de atração
lblLights=Luzes

View File

@@ -3437,4 +3437,17 @@ lblRelease=发布
lblSnapshot=快照
lblNewSnapshotVersion=新 FORGE-{0}!
cbSnapshotUpdate=启动时检查快照更新。
nlSnapshotUpdate=启用后,启动时自动检查快照更新并在标题栏上显示通知。
nlSnapshotUpdate=启用后,启动时自动检查快照更新并在标题栏上显示通知。
lblAddCommander=
lblaspartnercommander=作为伙伴指挥官
lblfromattractiondeck=从景点甲板
lbltoattractiondeck=到景点甲板
lblAddDeckSection=添加变体部分...
lblAddDeckSectionSelect=选择要添加的部分
lblFrom=
lblAttractions=景点
lblConspiracies=阴谋
lblChooseOrderCardsPutIntoAttractionDeck=选择放入景点牌组的卡片顺序
lblAttractionRollResult={0} 滚动访问他们的景点。结果:{1}。
lblAttractionDeckZone=吸引力甲板
lblLights=