From 925d347b0d5d1492cf1a8bda02c8cfe1ce2b8ef4 Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Fri, 28 Apr 2017 18:27:03 -0400 Subject: [PATCH] v0.6.0: implemented a loop builder, tied it into sewer boss level --- .../levels/SewerBossLevel.java | 26 +- .../levels/builders/LoopBuilder.java | 228 ++++++++++++++++++ .../levels/rooms/Room.java | 11 + 3 files changed, 256 insertions(+), 9 deletions(-) create mode 100644 core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/builders/LoopBuilder.java diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/SewerBossLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/SewerBossLevel.java index 7f3f9b44c..146340610 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/SewerBossLevel.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/SewerBossLevel.java @@ -29,8 +29,10 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; import com.shatteredpixel.shatteredpixeldungeon.items.Heap; import com.shatteredpixel.shatteredpixeldungeon.items.Item; import com.shatteredpixel.shatteredpixeldungeon.levels.builders.Builder; +import com.shatteredpixel.shatteredpixeldungeon.levels.builders.LoopBuilder; import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room; import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.RatKingRoom; +import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.EmptyRoom; import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.SewerBossEntranceRoom; import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.StandardRoom; import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; @@ -55,7 +57,7 @@ public class SewerBossLevel extends SewerLevel { int standards = standardRooms(); for (int i = 0; i < standards; i++) - initRooms.add(StandardRoom.createRoom()); + initRooms.add(new EmptyRoom()); initRooms.add(new RatKingRoom()); return initRooms; @@ -67,16 +69,18 @@ public class SewerBossLevel extends SewerLevel { } protected Builder builder(){ - //TODO want to use a more simple circular builder here - return super.builder(); + return new LoopBuilder() + .setPathLength(1f, new float[]{1}) + .setTunnelLength(new float[]{2, 3}, new float[]{1}); } @Override protected void placeSign() { while (true) { - int pos = pointToCell(roomEntrance.random()); + int pos = pointToCell(roomEntrance.random(2)); if (map[pos] != Terrain.LOCKED_EXIT - && map[pos] != Terrain.WALL_DECO) { + && map[pos] != Terrain.WALL_DECO + && map[pos] != Terrain.ENTRANCE) { map[pos] = Terrain.SIGN; break; } @@ -112,8 +116,8 @@ public class SewerBossLevel extends SewerLevel { Mob mob = Bestiary.mob( Dungeon.depth ); Room room; do { - room = Random.element(rooms); - } while (!(room instanceof StandardRoom) || room == roomEntrance); + room = randomRoom(StandardRoom.class); + } while (room == roomEntrance); mob.pos = pointToCell(room.random()); mobs.add( mob ); } @@ -129,14 +133,18 @@ public class SewerBossLevel extends SewerLevel { int pos; do { pos = pointToCell(roomEntrance.random()); - } while (pos == entrance || map[pos] == Terrain.SIGN); + } while (pos == entrance || map[pos] == Terrain.SIGN || solid[pos]); drop( item, pos ).type = Heap.Type.REMAINS; } } @Override public int randomRespawnCell() { - return pointToCell(roomEntrance.random()); + int pos; + do { + pos = pointToCell(roomEntrance.random()); + } while (pos == entrance || map[pos] == Terrain.SIGN || solid[pos]); + return pos; } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/builders/LoopBuilder.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/builders/LoopBuilder.java new file mode 100644 index 000000000..563e6803d --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/builders/LoopBuilder.java @@ -0,0 +1,228 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2017 Evan Debenham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +package com.shatteredpixel.shatteredpixeldungeon.levels.builders; + +import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room; +import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.ShopRoom; +import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.EntranceRoom; +import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.ExitRoom; +import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.StandardRoom; +import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.tunnel.TunnelRoom; +import com.watabou.utils.Random; + +import java.util.ArrayList; + +//A builder with one core loop as its primary element +public class LoopBuilder extends Builder { + + //TODO customizeable equation for angle changes (currently we're just linear) + + //path length is the percentage of pathable rooms that are on the loop + private float pathLength = 0.1f; + //The chance weights for extra rooms to be added to the path + private float[] pathLenJitterChances = new float[]{0, 2, 1}; + + public LoopBuilder setPathLength( float len, float[] jitter ){ + pathLength = len; + pathLenJitterChances = jitter; + return this; + } + + private float[] pathTunnelChances = new float[]{2, 3, 1}; + private float[] branchTunnelChances = new float[]{3, 2, 1}; + + public LoopBuilder setTunnelLength( float[] path, float[] branch){ + pathTunnelChances = path; + branchTunnelChances = branch; + return this; + } + + private float extraConnectionChance = 0.1f; + + public LoopBuilder setExtraConnectionChance( float chance ){ + extraConnectionChance = chance; + return this; + } + + @Override + public ArrayList build(ArrayList rooms) { + + Room entrance = null; + Room exit = null; + Room shop = null; + + ArrayList multiConnections = new ArrayList<>(); + ArrayList singleConnections = new ArrayList<>(); + + for (Room r : rooms){ + if (r instanceof EntranceRoom){ + entrance = r; + } else if (r instanceof ExitRoom) { + exit = r; + } else if (r instanceof ShopRoom && r.maxConnections(Room.ALL) == 1){ + shop = r; + } else if (r.maxConnections(Room.ALL) > 1){ + multiConnections.add(r); + } else if (r.maxConnections(Room.ALL) == 1){ + singleConnections.add(r); + } + } + + if (entrance == null){ + return null; + } + + entrance.setSize(); + entrance.setPos(0, 0); + + float startAngle = Random.Float(0, 360); + + ArrayList loop = new ArrayList<>(); + int roomsOnLoop = (int)(multiConnections.size()*pathLength) + Random.chances(pathLenJitterChances); + roomsOnLoop = Math.min(roomsOnLoop, multiConnections.size()); + if (exit != null) roomsOnLoop++; + roomsOnLoop++; + + for (int i = 0; i < roomsOnLoop; i++){ + if (i == 0) + loop.add(entrance); + else if (exit != null && i == roomsOnLoop/2) + loop.add(exit); + else + loop.add(multiConnections.remove(0)); + + int tunnels = Random.chances(pathTunnelChances); + for (int j = 0; j < tunnels; j++){ + loop.add(new TunnelRoom()); + } + } + + Room prev = entrance; + float targetAngle = startAngle; + float angleChange = 360f / loop.size(); + for (int i = 0; i < loop.size(); i++){ + Room r = loop.get(i); + targetAngle += angleChange; + float placeAngle; + if ((placeAngle = placeRoom(rooms, prev, r, targetAngle)) != -1) { + //targetAngle += (targetAngle - placeAngle); + prev = r; + if (!rooms.contains(prev)) + rooms.add(prev); + } + } + + //FIXME this is lazy, there are ways to do this without relying on chance + if (!prev.connect(entrance)){ + return null; + } + + ArrayList branchable = new ArrayList<>(); + for (Room r : loop){ + if (r instanceof StandardRoom) branchable.add(r); + } + + int i = 0; + + Room curr; + float angle; + int tries; + ArrayList tunnelsThisBranch = new ArrayList<>(); + //TODO this is almost identical to logic in linebuilder, can probably generalize + while (i < multiConnections.size() + singleConnections.size()){ + + tunnelsThisBranch.clear(); + curr = Random.element(branchable); + + int tunnels = Random.chances(branchTunnelChances); + for (int j = 0; j < tunnels; j++){ + TunnelRoom t = new TunnelRoom(); + tries = 10; + + do { + angle = placeRoom(rooms, curr, t, Random.Float(360f)); + tries--; + } while (angle == -1 && tries >= 0); + + if (angle == -1) { + for (Room r : tunnelsThisBranch){ + r.clearConnections(); + rooms.remove(r); + } + tunnelsThisBranch.clear(); + break; + } else { + tunnelsThisBranch.add(t); + rooms.add(t); + } + + curr = t; + } + + if (tunnelsThisBranch.size() != tunnels){ + continue; + } + + Room r; + if (i < multiConnections.size()) { + r = multiConnections.get(i); + } else { + r = singleConnections.get(i - multiConnections.size()); + } + + tries = 10; + + do { + angle = placeRoom(rooms, curr, r, Random.Float(360f)); + tries--; + } while (angle == -1 && tries >= 0); + + if (angle == -1){ + for (Room t : tunnelsThisBranch){ + t.clearConnections(); + rooms.remove(t); + } + tunnelsThisBranch.clear(); + continue; + } + + + if (r.maxConnections(Room.ALL) > 1 && Random.Int(2) == 0) + branchable.add(r); + + i++; + } + + findNeighbours(rooms); + + for (Room r : rooms){ + for (Room n : r.neigbours){ + if (!n.connected.containsKey(r) + && Random.Float() < extraConnectionChance){ + r.connect(n); + } + } + } + + return rooms; + } +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/Room.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/Room.java index 0e4ce5720..416531dae 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/Room.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/Room.java @@ -250,6 +250,17 @@ public class Room extends Rect implements Graph.Node, Bundlable { return false; } + public void clearConnections(){ + for (Room r : neigbours){ + r.neigbours.remove(this); + } + neigbours.clear(); + for (Room r : connected.keySet()){ + r.connected.remove(this); + } + connected.clear(); + } + // **** Graph.Node interface **** @Override