package greymerk.roguelike.dungeon; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; import greymerk.roguelike.config.RogueConfig; import greymerk.roguelike.dungeon.base.IDungeonRoom; import greymerk.roguelike.dungeon.settings.LevelSettings; import greymerk.roguelike.worldgen.Cardinal; import greymerk.roguelike.worldgen.Coord; import greymerk.roguelike.worldgen.IWorldEditor; public class LevelGeneratorClassic implements ILevelGenerator{ private IWorldEditor editor; private Random rand; private IDungeonLevel level; private List<DungeonNode> nodes; private List<DungeonTunnel> tunnels; private DungeonNode end; public LevelGeneratorClassic(IWorldEditor editor, Random rand, IDungeonLevel level){ this.editor = editor; this.rand = rand; this.level = level; nodes = new ArrayList<DungeonNode>(); tunnels = new ArrayList<DungeonTunnel>(); } public void generate(Coord start, DungeonNode oldEnd){ List<Node> gNodes = new ArrayList<Node>(); Node startNode = new Node(this, level.getSettings(), Cardinal.directions[rand.nextInt(Cardinal.directions.length)], start); gNodes.add(startNode); while(!this.isDone(gNodes)){ this.update(gNodes); } for(Node n : gNodes){ n.cull(); } DungeonNode startDungeonNode = null; for(Node n : gNodes){ DungeonNode nToAdd = n.createNode(); if(n == startNode){ startDungeonNode = nToAdd; } this.nodes.add(nToAdd); this.tunnels.addAll(n.createTunnels(editor)); } int attempts = 0; do{ end = this.nodes.get(rand.nextInt(this.nodes.size())); attempts++; } while(end == startDungeonNode || end.getPosition().distance(start) > (16 + attempts * 2)); List<DungeonNode> nodes = this.getNodes(); Collections.shuffle(nodes, rand); // assign dungeons for (DungeonNode node : nodes){ if(node == end || node == startDungeonNode) continue; // TODO: Find way to check available space when picking room IDungeonRoom toGenerate = this.level.getSettings().getRooms().get(rand); node.setDungeon(toGenerate); } if(RogueConfig.getBoolean(RogueConfig.ENCASE)){ for (DungeonNode node : nodes){ if(node == end || node == startDungeonNode) continue; node.encase(editor, rand, this.level.getSettings().getTheme()); } for(DungeonTunnel t : this.getTunnels()){ t.encase(editor, rand, this.level.getSettings().getTheme()); } } for(DungeonTunnel t : this.getTunnels()){ t.construct(editor, rand, this.level.getSettings()); } for (DungeonNode node : nodes){ if(node == end || node == startDungeonNode) continue; IDungeonRoom toGenerate = node.getRoom(); toGenerate.generate(editor, rand, this.level.getSettings(), node.getEntrances(), node.getPosition()); } for(DungeonTunnel tunnel : this.getTunnels()){ tunnel.genSegments(editor, rand, this.level); } LevelGenerator.generateLevelLink(editor, rand, this.level.getSettings(), start, oldEnd); } public void update(List<Node> nodes){ if(!this.full(nodes)){ for (int i = 0; i < nodes.size(); i++){ nodes.get(i).update(nodes); } } } private boolean isDone(List<Node> nodes){ boolean allDone = true; for(Node node : nodes){ if(!node.isDone()){ allDone = false; } } return allDone || this.full(nodes); } private boolean full(List<Node> nodes){ return nodes.size() >= this.level.getSettings().getNumRooms(); } public void spawnNode(List<Node> nodes, Tunneler tunneler){ Node toAdd = new Node(this, this.level.getSettings(), tunneler.getDirection(), tunneler.getPosition()); nodes.add(toAdd); } public boolean hasNearbyNode(List<Node> nodes, Coord pos, int min){ for (Node node : nodes){ int dist = (int) node.getPos().distance(pos); if(dist < min){ return true; } } return false; } public IDungeonLevel getLevel(){ return this.level; } @Override public List<DungeonNode> getNodes() { return this.nodes; } @Override public List<DungeonTunnel> getTunnels() { return this.tunnels; } public DungeonNode getEnd(){ return this.end; } private class Tunneler{ private boolean done; private Cardinal dir; private LevelSettings settings; private LevelGeneratorClassic generator; private Coord start; private Coord end; private int extend; public Tunneler(Cardinal dir, LevelSettings settings, LevelGeneratorClassic generator, Coord start){ this.done = false; this.dir = dir; this.settings = settings; this.generator = generator; this.start = new Coord(start); this.end = new Coord(start); this.extend = settings.getScatter() * 2; } public void update(List<Node> nodes){ if(this.done){ return; } if(this.generator.hasNearbyNode(nodes, end, settings.getScatter())){ end.add(dir); } else { if(rand.nextInt(extend) == 0){ generator.spawnNode(nodes, this); this.done = true; } else { end.add(dir); extend--; } } } public boolean isDone(){ return this.done; } public Cardinal getDirection(){ return this.dir; } public Coord getPosition(){ return new Coord(this.end); } public DungeonTunnel createTunnel(){ return new DungeonTunnel(editor, new Coord(this.start), new Coord(this.end), this.dir); } } private class Node{ private List<Tunneler> tunnelers; private LevelGeneratorClassic level; private LevelSettings settings; private Cardinal direction; private Coord pos; public Node(LevelGeneratorClassic level, LevelSettings settings, Cardinal direction, Coord pos){ this.tunnelers = new ArrayList<Tunneler>(); this.level = level; this.settings = settings; this.direction = direction; this.pos = pos; this.spawnTunnelers(); } private void spawnTunnelers(){ if(!this.level.level.inRange(pos)){ return; } for(Cardinal dir : Cardinal.directions){ if (dir.equals(Cardinal.reverse(this.direction))){ continue; } this.tunnelers.add(new Tunneler(dir, this.settings, this.level, new Coord(this.pos))); } } public void update(List<Node> nodes){ for (Tunneler tunneler : tunnelers){ tunneler.update(nodes); } } public boolean isDone(){ for (Tunneler tunneler : tunnelers){ if(!tunneler.isDone()){ return false; } } return true; } public Coord getPos(){ return new Coord(this.pos); } public Cardinal[] getEntrances(){ List<Cardinal> c = new ArrayList<Cardinal>(); c.add(Cardinal.reverse(this.direction)); for(Tunneler t : this.tunnelers){ c.add(t.dir); } return c.toArray(new Cardinal[c.size()]); } public List<DungeonTunnel> createTunnels(IWorldEditor editor){ List<DungeonTunnel> tunnels = new ArrayList<DungeonTunnel>(); for(Tunneler t : this.tunnelers){ tunnels.add(t.createTunnel()); } return tunnels; } public DungeonNode createNode(){ return new DungeonNode(this.getEntrances(), this.pos); } public void cull(){ List<Tunneler> toKeep = new ArrayList<Tunneler>(); for(Tunneler t : this.tunnelers){ if(t.done){ toKeep.add(t); } } this.tunnelers = toKeep; } } }