package commands; import java.util.Random; import core.Command; import core.LocStrings; import dataStructures.KittyChannel; import dataStructures.KittyGuild; import dataStructures.KittyRating; import dataStructures.KittyRole; import dataStructures.KittyUser; import dataStructures.Response; import dataStructures.UserInput; import utils.OptionParser; // This command is a port of worldGen command line program by Matthew Cech (https://www.matthewcech.com/) // Permissions were granted for use and modification in this bot. public class CommandMap extends Command { final int MaxWidth = 50; final int MaxHeight = 35; public CommandMap(KittyRole roleLevel, KittyRating contentRating) { super(roleLevel, contentRating); } @Override public String getHelpText() { return String.format(LocStrings.stub("MapInfo"), "" + MaxWidth, "" + MaxHeight); } @Override public void onRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res) { // Variables int width = 35; int height = 25; long seed = 0; String header = ""; String body = ""; Random randGenerator = null; // Parse out a seed if one was provided boolean hasSeed = false; if(input.args != null && input.args.length() > 0) try { OptionParser parser = new OptionParser(input.args); String seedStr = parser.getOption("-s", true); if(seedStr != null) { seed = Long.parseLong(seedStr); hasSeed = true; } String widthStr = parser.getOption("-w", true); if(widthStr != null) width = validateSize(Integer.parseInt(widthStr), MaxWidth); String heightStr = parser.getOption("-h", true); if(heightStr != null) height = validateSize(Integer.parseInt(heightStr), MaxHeight); } catch(NumberFormatException e) { res.send(LocStrings.stub("MapInvalid")); return; } // Set up the random generator, creating a seed if needed. if(hasSeed) { randGenerator = new Random(seed); } else { seed = new Random().nextLong(); randGenerator = new Random(seed); } // Response header creation header += LocStrings.stub("MapVersion") + "\n"; header += LocStrings.stub("MapSeed") + ": `" + seed + "`, "; header += LocStrings.stub("MapWidth") + "`"+ width +"`, "; header += LocStrings.stub("MapHeight") + ": `"+ height + "`\n"; // Response body creation body += "```\n"; body += generateMap(width, height, randGenerator); body += "\n```"; // Send back the map res.send(header + body); } // Verifies and appropriately caps input as necessary int validateSize(int input, int max) { if(input < 0) throw new NumberFormatException(); if(input > max) input = max; return input; } // Does the map generation String generateMap(int width, int height, Random gen) { String landscape[][] = new String[width][height]; int r1 = rand(gen); // Variables to determine various feature quantities. int LandScarcity = (r1 % 200) + 100; // Higher = more water. int landPasses = 7; // Higher = larger land chunks. int MountainScarcity = 50; // Higher = fewer mountains; int mountainPasses = r1 % 8 + 2; // Times to plant mountains. int marshPasses = r1 % 3 + 10; // Times to plant marshes. int lavaPasses = 1; // Times to plant lava. int beachPasses = 1; int i, j; int sizeX = height; int sizeY = width; // Previously defines String LAND = "\u02F6"; // Used to be " String WATER = " "; // Used to be ~ String MOUNTAIN = "\u25B2"; // Used to be ^ String BEACH = "="; // Used to be ~ String MARSH = "\u00A5"; // Used to be , String LAVA = "#"; // Used to be * //String SPACE = " "; //////////////////// //GENERATION LOOPS// //////////////////// // Populate grid with water. for(i = 0; i < sizeY; i++) for(j = 0; j < sizeX; j++) landscape[i][j] = WATER; // land initial distribution. for(i = 0; i < sizeY; i++) for(j = 0; j < sizeX; j++) { r1 = rand(gen) % LandScarcity; if(r1 == 0) landscape[i][j] = LAND; } // Land expansion, standard land. while(landPasses-- > 0) for(i = 0; i < sizeY; i++) for(j = 0; j < sizeX; j++) if(landscape[i][j] == LAND) { if((rand(gen) % 3) == 0) expandLand(sizeX, sizeY, landscape, i, j, LAND); } //Beach creation based on land while(beachPasses-- > 0) for(i = 0; i < sizeY; i++) for(j = 0; j < sizeX; j++) if(landscape[i][j] == LAND) { Surround beachQuals = new Surround(new String[]{WATER, WATER, WATER, WATER}); if((rand(gen) % 3) == 0) replaceIf(sizeX, sizeY, landscape, beachQuals, i, j, BEACH); } //Mountain creation based on land for(i = 0; i < sizeY; i++) for(j = 0; j < sizeX; j++) if(landscape[i][j] == LAND) { Surround MountainQuals = new Surround(new String[]{LAND, LAND, LAND, LAND}); if((rand(gen) % MountainScarcity) == 0) { replaceIf(sizeX, sizeY, landscape, MountainQuals, i, j, MOUNTAIN); landscape[i][j] = MOUNTAIN; } } //Mountain creation based on Mountain while(mountainPasses-- > 0) for(i = 0; i < sizeY; i++) for(j = 0; j < sizeX; j++) if(landscape[i][j] == LAND) { Surround MountainQuals = new Surround(new String[]{MOUNTAIN, MOUNTAIN, MOUNTAIN, MOUNTAIN}); int scalar = 0; scalar = replaceIf(sizeX, sizeY, landscape, MountainQuals, i, j, MOUNTAIN); if(scalar > 0) if((rand(gen) % (30 / scalar)) == 0) landscape[i][j] = MOUNTAIN; } //Marsh creation based on land while(marshPasses-- > 0) for(i = 0; i < sizeY; i++) for(j = 0; j < sizeX; j++) if( landscape[i][j] == LAND) { Surround ForestQuals = new Surround(); ForestQuals.left = BEACH; ForestQuals.right = BEACH; ForestQuals.up = BEACH; ForestQuals.down = BEACH; if((rand(gen) % 80) == 0) { replaceIf(sizeX, sizeY, landscape, ForestQuals, i, j, MARSH); landscape[i][j] = MARSH; } } //Lava creation based on land while(lavaPasses-- > 0) for(i = 0; i < sizeY; i++) for(j = 0; j < sizeX; j++) if( landscape[i][j] == MOUNTAIN) { Surround Mountains = new Surround(new String[]{MOUNTAIN, MOUNTAIN, MOUNTAIN, MOUNTAIN}); if((rand(gen) % 5) == 0) if(replaceIf(sizeX, sizeY, landscape, Mountains, i, j, MOUNTAIN) == 4) landscape[i][j] = LAVA; } //////////// //CLEAN UP// //////////// //Remove lone islands, with the exception of things on the edge. for(i = 0; i < sizeY; i++) for(j = 0; j < sizeX; j++) { Surround water = new Surround(new String[]{WATER, WATER, WATER, WATER}); if(replaceIf(sizeX, sizeY, landscape, water, i, j, WATER) == 4) landscape[i][j] = WATER; } // Build the map String output = ""; for(int y = 0; y < height; ++y) { if(y != 0) output += '\n'; for(int x = 0; x < width; ++x) output += landscape[x][y]; } // Return the map return output; } // Standardized application of rand for the purposes of this generaton int rand(Random rand) { return rand.nextInt(); } // Ported from C struct class Surround { public String left; public String right; public String up; public String down; public Surround() { left = null; right = null; up = null; down = null; } public Surround(String[] args) { if(args.length > 0) left = args[0]; if(args.length > 1) right = args[1]; if(args.length > 2) up = args[2]; if(args.length > 3) down = args[3]; } } // Expands the current land block static void expandLand(int x, int y, String landscape[][], int i, int j, String toAdd) { // Four checks, one for each block in the 4 cardinal directions. if(i - 1 > -1) landscape[i - 1][j] = toAdd; if(i + 1 < y) landscape[i + 1][j] = toAdd; if(j - 1 > -1) landscape[i][j - 1] = toAdd; if(j + 1 < x) landscape[i][j + 1] = toAdd; } // Replaces surrounding stuff int replaceIf(int x, int y, String[][] landscape, Surround spaces, int i, int j, String toAdd) { // Running total of tiles changed. int tilesChanged = 0; // Check square left of me. if(spaces.left != null) if(j - 1 > -1) if(landscape[i][j - 1] == spaces.left) { landscape[i][j - 1] = toAdd; tilesChanged++; } // Check square right of me. if(spaces.right != null) if(j + 1 < x) if(landscape[i][j + 1] == spaces.right) { landscape[i][j + 1] = toAdd; tilesChanged++; } // Check square above me. if(spaces.up != null) if(i - 1 > -1) if(landscape[i - 1][j] == spaces.up) { landscape[i - 1][j] = toAdd; tilesChanged++; } // Check square below me. if(spaces.down != null) if(i + 1 < y) if(landscape[i + 1][j] == spaces.down) { landscape[i + 1][j] = toAdd; tilesChanged++; } return tilesChanged; } }