/** * Copyright 2014 * SMEdit https://github.com/StarMade/SMEdit * SMTools https://github.com/StarMade/SMTools * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. **/ package jo.sm.plugins.planet.gen; import java.util.Random; import jo.sm.data.SparseMatrix; import jo.sm.data.StarMade; import jo.sm.logic.PluginUtils; import jo.sm.mods.IBlocksPlugin; import jo.sm.mods.IPluginCallback; import jo.sm.ship.data.Block; import jo.vecmath.logic.MathUtils; /** * @Auther Jo Jaquinta for SMEdit Classic - version 1.0 **/ public class GiantsCausewayPlugin implements IBlocksPlugin { public static final String NAME = "Shape/Giant's Causeway"; public static final String DESC = "Hexagonal slab surface"; public static final String AUTH = "Jo Jaquinta"; public static final int[][] CLASSIFICATIONS = { {TYPE_PLANET, SUBTYPE_GENERATE, 26},}; @Override public String getName() { return NAME; } @Override public String getDescription() { return DESC; } @Override public String getAuthor() { return AUTH; } @Override public Object newParameterBean() { return new GiantsCausewayParameters(); } @Override public void initParameterBean(SparseMatrix<Block> original, Object params, StarMade sm, IPluginCallback cb) { } @Override public int[][] getClassifications() { return CLASSIFICATIONS; } @Override public SparseMatrix<Block> modify(SparseMatrix<Block> original, Object p, StarMade sm, IPluginCallback cb) { GiantsCausewayParameters params = (GiantsCausewayParameters) p; HexParams hParams = makeParams(params); cb.setStatus("Generating terrain"); cb.startTask(hParams.fillTarget); while (hParams.fillTarget > 0) { fillAndMove(hParams); cb.workTask(1); } cb.endTask(); findBounds(hParams); fillGrid(hParams, params, cb); //printGrid(hParams, params); return hParams.grid; } private void fillGrid(HexParams hparams, GiantsCausewayParameters params, IPluginCallback cb) { cb.setStatus("Filling in terrain"); cb.startTask(params.getPlanetRadius() * 2); int[] xy = new int[2]; for (xy[0] = -params.getPlanetRadius(); xy[0] <= params.getPlanetRadius(); xy[0]++) { cb.workTask(1); for (xy[1] = -params.getPlanetRadius(); xy[1] <= params.getPlanetRadius(); xy[1]++) { fillColumn(xy, hparams, params); } } cb.endTask(); } private void fillColumn(int[] blockXY, HexParams hparams, GiantsCausewayParameters params) { double r = Math.sqrt(blockXY[0] * blockXY[0] + blockXY[1] * blockXY[1]); if (r > params.getPlanetRadius()) { return; // out of radius } int[] hexXY = findNearestHex(blockXY, hparams, params); int hexHeight = hparams.field[hexXY[0]][hexXY[1]]; int columnHeight = (int) MathUtils.interpolate(hexHeight, hparams.elevLow, hparams.elevHigh, 0, params.getPlanetHeight()); PluginUtils.fill(hparams.grid, blockXY[0], 0, blockXY[1], blockXY[0], columnHeight, blockXY[1], params.getFillWith(), 0); } private int[] findNearestHex(int[] blockXY, HexParams hparams, GiantsCausewayParameters params) { int approxX = (blockXY[0] + params.getPlanetRadius()) / hparams.hexWidth; int approxY = (blockXY[1] + params.getPlanetRadius()) / hparams.hexHeight; int[] bestXY = new int[]{-1, -1}; int bestV = 0; int[] testXY = new int[2]; int[] testBlockXY = new int[2]; for (int x = -2; x <= 2; x++) { for (int y = -2; y <= 2; y++) { testXY[0] = approxX + x; if ((testXY[0] < 0) || (testXY[0] >= hparams.fieldWidth)) { continue; } testXY[1] = approxY + y; if ((testXY[1] < 0) || (testXY[1] >= hparams.fieldHeight)) { continue; } getHexPosition(testXY, testBlockXY, hparams, params); int v = dist(testBlockXY, blockXY); if ((bestXY[0] < 0) || (v < bestV)) { bestXY[0] = testXY[0]; bestXY[1] = testXY[1]; bestV = v; } } } return bestXY; } private void getHexPosition(int[] hexXY, int[] blockXY, HexParams hparams, GiantsCausewayParameters params) { blockXY[0] = hexXY[0] * hparams.hexWidth + params.getHexRadius(); blockXY[1] = hexXY[1] * hparams.hexHeight + hparams.hexHeight / 2; if (hexXY[0] % 2 == 1) { blockXY[1] += hparams.hexHeight / 2; } blockXY[0] -= params.getPlanetRadius(); blockXY[1] -= params.getPlanetRadius(); } private int dist(int[] p1, int[] p2) { return (int) Math.sqrt((p2[0] - p1[0]) * (p2[0] - p1[0]) + (p2[1] - p1[1]) * (p2[1] - p1[1])); } private void findBounds(HexParams hparams) { hparams.elevHigh = hparams.field[0][0]; hparams.elevLow = hparams.field[0][0]; for (int y = 0; y < hparams.fieldHeight; y++) { for (int x = 0; x < hparams.fieldWidth; x++) { hparams.elevHigh = Math.max(hparams.elevHigh, hparams.field[x][y]); hparams.elevLow = Math.min(hparams.elevLow, hparams.field[x][y]); } } } // private void printGrid(HexParams hparams, GiantsCausewayParameters params) // { // int[] fieldXY = new int[2]; // int[] blockXY = new int[2]; // for (fieldXY[1] = 0; fieldXY[1] < hparams.fieldHeight; fieldXY[1]++) // { // for (fieldXY[0] = 0; fieldXY[0] < hparams.fieldWidth; fieldXY[0]++) // { // getHexPosition(fieldXY, blockXY, hparams, params); // System.out.print(blockXY[0]+","+blockXY[1]+"\t"); // } // System.out.println(); // } // } private void fillAndMove(HexParams hparams) { // fill fill(hparams, 0, 0, 8); fill(hparams, 0, -1, 4); fill(hparams, 0, 1, 4); fill(hparams, -1, 0, 4); fill(hparams, 1, 0, 4); if (hparams.fillXY[0] % 2 == 0) { fill(hparams, -1, -1, 4); fill(hparams, 1, -1, 4); } else { fill(hparams, -1, 1, 4); fill(hparams, 1, 1, 4); } fill(hparams, 0, -2, 2); fill(hparams, 0, 2, 2); fill(hparams, -2, 0, 2); fill(hparams, 2, 0, 2); fill(hparams, -2, -1, 2); fill(hparams, -2, 1, 2); fill(hparams, 2, -1, 2); fill(hparams, 2, 1, 2); if (hparams.fillXY[0] % 2 == 0) { fill(hparams, -1, -1, 2); fill(hparams, -1, 2, 2); fill(hparams, 1, -1, 2); fill(hparams, 1, 2, 2); } else { fill(hparams, -1, -2, 2); fill(hparams, -1, 1, 2); fill(hparams, 1, -2, 2); fill(hparams, 1, 1, 2); } // move switch (hparams.rnd.nextInt(6)) { case 0: hparams.fillXY[1]--; break; case 1: hparams.fillXY[1]++; break; case 2: hparams.fillXY[0]--; break; case 3: hparams.fillXY[0]++; break; case 4: hparams.fillXY[0]++; if (hparams.fillXY[0] % 2 == 0) { hparams.fillXY[1]--; } else { hparams.fillXY[1]++; } break; case 5: hparams.fillXY[0]--; if (hparams.fillXY[0] % 2 == 0) { hparams.fillXY[1]--; } else { hparams.fillXY[1]++; } break; } normalize(hparams, hparams.fillXY); hparams.fillTarget--; } private void fill(HexParams hparams, int dx, int dy, int amnt) { int[] xy = new int[]{hparams.fillXY[0] + dx, hparams.fillXY[1] + dy}; normalize(hparams, xy); hparams.field[xy[0]][xy[1]] += amnt; } private void normalize(HexParams hparams, int[] xy) { if (xy[0] < 0) { xy[0] += hparams.fieldWidth; } else if (xy[0] >= hparams.fieldWidth) { xy[0] -= hparams.fieldWidth; } if (xy[1] < 0) { xy[1] += hparams.fieldHeight; } else if (xy[1] >= hparams.fieldHeight) { xy[1] -= hparams.fieldHeight; } } private HexParams makeParams(GiantsCausewayParameters params) { HexParams hparams = new HexParams(); hparams.grid = new SparseMatrix<Block>(); hparams.hexWidth = 3 * params.getHexRadius() / 2; hparams.hexHeight = (int) (Math.sqrt(3) * params.getHexRadius()); hparams.fieldWidth = params.getPlanetRadius() * 2 / hparams.hexWidth + 1; hparams.fieldHeight = params.getPlanetRadius() * 2 / hparams.hexHeight + 1; hparams.field = new int[hparams.fieldWidth][hparams.fieldHeight]; hparams.fillTarget = hparams.fieldWidth * hparams.fieldHeight * 2; hparams.rnd = new Random(); hparams.fillXY = new int[]{hparams.rnd.nextInt(hparams.fieldWidth), hparams.rnd.nextInt(hparams.fieldHeight)}; return hparams; } class HexParams { SparseMatrix<Block> grid; int hexWidth; int hexHeight; int fieldWidth; int fieldHeight; int[][] field; int fillTarget; Random rnd; int[] fillXY; int elevLow; int elevHigh; } }