RegionRandom.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /// <reference path="../Region.ts" />
  2. /// <reference path="../Shuffler.ts" />
  3. /// <reference path="ShufflerDirection.ts" />
  4. /// <reference path="RoomRandomMap.ts" />
  5. /// <reference path="RoomRandom.ts" />
  6. /// <reference path="RoomRandomFodder.ts" />
  7. interface RandomizingRoomOptions {
  8. region : RegionRandom;
  9. room : RoomRandom;
  10. map : RoomRandomMap;
  11. }
  12. class RegionRandom extends Region {
  13. private randomized = false;
  14. public map : RoomRandomMap;
  15. public fodderRoomClass : typeof RoomRandom = RoomRandomFodder;
  16. public placedRooms : Array<RoomRandom> = [];
  17. public constructor (name : string, map? : RoomRandomMap) {
  18. super(name);
  19. this.map = map == undefined ? new RoomRandomMap() : map;
  20. }
  21. public async randomize () {
  22. if (!this.randomized) {
  23. await RegionRandom.rulebookRandomizeRegion.execute({
  24. noun : this
  25. });
  26. }
  27. }
  28. public placeAt (room : RoomRandom, x : number, y : number) {
  29. this.map.map(room, x, y);
  30. this.place(room);
  31. }
  32. public static rng : () => number = () => { return Math.random(); };
  33. public static rulebookRandomizeRegion = new Rulebook<Region>("Randomizing Random Region something");
  34. public static rulebookPlaceRoom = new Rulebook<RandomizingRoomOptions>("Placing Random Room something");
  35. public static rulebookBeforePlaceRoom = new Rulebook<RandomizingRoomOptions>("Before placing Random Room something");
  36. public static rulebookAfterPlaceRoom = new Rulebook<RandomizingRoomOptions>("After placing Random Room something");
  37. public static ruleFirstRandomizeRegion = RegionRandom.rulebookRandomizeRegion.createAndAddRule({
  38. name : "Empty map cache to start randomizing region",
  39. firstPriority : Rule.PRIORITY_HIGHEST,
  40. code : runner => {
  41. let region = <RegionRandom> runner.noun;
  42. // We don't want to connect a region to another region unintentionally
  43. region.map.emptyCache();
  44. }
  45. });
  46. public static ruleBasicRandomizeRegion = RegionRandom.rulebookRandomizeRegion.createAndAddRule({
  47. name : "Randomize all unplaced, randomizable rooms in region something",
  48. code : async runner => {
  49. let region = <RegionRandom> runner.noun;
  50. let roomShuffler = new Shuffler(
  51. Region.InRelation.getAllRightTypes(region, RoomRandom).filter((room : RoomRandom) => {
  52. return room.randomizable && !room.placed &&
  53. (room == WorldState.player.getRoom() || (RegionRandom.rng() * 100) <= room.appearChance);
  54. }), RegionRandom.rng
  55. );
  56. for (let room = roomShuffler.getOne(); room != undefined; room = roomShuffler.getOne()) {
  57. // Prevent available connections from getting too low
  58. // If we're placing rooms with too few availableConnections, we might end up reaching 0 availableConnections
  59. // Which would mean no more rooms can be placed.
  60. while (region.map.rooms > 0 && region.map.getAvailableConnections() < 4) {
  61. let fodder = new region.fodderRoomClass();
  62. let options = <RandomizingRoomOptions> {
  63. map : region.map,
  64. room : fodder,
  65. region : region
  66. };
  67. region.place(fodder);
  68. await RegionRandom.rulebookBeforePlaceRoom.execute({noun : options});
  69. await RegionRandom.rulebookPlaceRoom.execute({noun : options});
  70. await RegionRandom.rulebookAfterPlaceRoom.execute({noun : options});
  71. }
  72. let options = <RandomizingRoomOptions> {
  73. map : region.map,
  74. room : room,
  75. region : region
  76. };
  77. await RegionRandom.rulebookBeforePlaceRoom.execute({noun : options});
  78. await RegionRandom.rulebookPlaceRoom.execute({noun : options});
  79. await RegionRandom.rulebookAfterPlaceRoom.execute({noun : options});
  80. if (!room.placed) {
  81. Elements.CurrentTurnHandler.printAsError(new Say("Was unable to place room ", room, ". Game might be unplayable."));
  82. }
  83. }
  84. }
  85. });
  86. public static ruleAddExtraConnections = RegionRandom.rulebookRandomizeRegion.createAndAddRule({
  87. firstPriority : Rule.PRIORITY_LOWEST,
  88. name : "Add extra connections to rooms in region",
  89. code : runner => {
  90. let region = <RegionRandom> runner.noun;
  91. let placedRooms = Region.InRelation.getAllRightTypes(region, RoomRandom).filter((room : RoomRandom) => {
  92. return room.randomizable && room.placed;
  93. });
  94. placedRooms.forEach((room : RoomRandom) => {
  95. let myCoordinates = region.map.getCoordinates(room);
  96. let directionShuffler = new Shuffler<number>(room.connectableOn.slice(0), RegionRandom.rng);
  97. for (let direction = directionShuffler.getOne(); direction != undefined && (RegionRandom.rng() * 100) <= room.extraConnectionChance; direction = directionShuffler.getOne()) {
  98. if (room.connections[direction] == undefined) {
  99. let otherCoordinates = Room.shift(myCoordinates, direction);
  100. let otherRoom = region.map.getRoom(otherCoordinates[0], otherCoordinates[1]);
  101. if (otherRoom != undefined && otherRoom.randomizable
  102. && otherRoom.isConnectableOn(OppositeDirection[Direction[direction]])
  103. && (RegionRandom.rng() * 100) <= otherRoom.extraConnectionChance) {
  104. room.mapRoom(otherRoom, direction);
  105. }
  106. }
  107. }
  108. });
  109. }
  110. });
  111. public static rulePlaceFirstRoom = RegionRandom.rulebookPlaceRoom.createAndAddRule({
  112. name : "Placing First room something",
  113. firstPriority : Rule.PRIORITY_HIGHEST,
  114. code : runner => {
  115. let placingOptions = <RandomizingRoomOptions> runner.noun;
  116. if (placingOptions.map.isFree(0, 0)) {
  117. placingOptions.map.map(placingOptions.room, 0, 0);
  118. return true;
  119. }
  120. },
  121. conditions : runner => {
  122. return runner.noun.map.rooms == 0;
  123. }
  124. });
  125. public static rulePlaceNonTrickyRoom = RegionRandom.rulebookPlaceRoom.createAndAddRule({
  126. name : "Placing non-tricky room something",
  127. firstPriority : Rule.PRIORITY_HIGH,
  128. code : runner => {
  129. let placingOptions = <RandomizingRoomOptions> runner.noun;
  130. //let directionShuffler = new Shuffler<number>(placingOptions.room.connectableOn);
  131. let preferredGrowthDirection = placingOptions.map.getPreferredGrowth();
  132. let directionShuffler = new ShufflerDirection(placingOptions.room.connectableOn, preferredGrowthDirection);
  133. //for (let direction = directionShuffler.getOne(); direction != undefined; direction = directionShuffler.getOne()) {
  134. for (let direction = directionShuffler.getDirection(); direction != undefined; direction = directionShuffler.getDirection()) {
  135. let oppositeDirection = OppositeDirection[Direction[direction]];
  136. let connectableRoom = placingOptions.map.getAnyFromCache(oppositeDirection);
  137. if (connectableRoom != undefined) {
  138. let otherCoordinates = placingOptions.map.getCoordinates(connectableRoom);
  139. let myCoordinates = Room.shift(otherCoordinates, oppositeDirection);
  140. placingOptions.room.mapRoom(connectableRoom, direction);
  141. placingOptions.map.map(placingOptions.room, myCoordinates[0], myCoordinates[1]);
  142. return true;
  143. }
  144. }
  145. },
  146. conditions : runner => {
  147. return runner.noun.room.trickyCode == undefined;
  148. }
  149. });
  150. public static rulePlaceTrickyRoom = RegionRandom.rulebookPlaceRoom.createAndAddRule({
  151. name : "Placing tricky room something",
  152. code : runner => {
  153. let placingOptions = <RandomizingRoomOptions> runner.noun;
  154. let placedRooms = <Array<RoomRandom>> placingOptions.region.getRooms().filter((room) => {
  155. return room instanceof RoomRandom && room.placed;
  156. });
  157. if (placedRooms.length == 0) {
  158. Elements.CurrentTurnHandler.printAsError("Unable to place room " + placingOptions.room.getPrintedName() + ": There are no rooms to connect to!");
  159. return false;
  160. }
  161. let roomShuffler = new Shuffler(placedRooms, RegionRandom.rng);
  162. for (let connectableRoom = roomShuffler.getOne(); connectableRoom != undefined; connectableRoom = roomShuffler.getOne()) {
  163. let trickier = <TrickierOptions> {
  164. region : placingOptions.region,
  165. map : placingOptions.map,
  166. otherRoom : connectableRoom
  167. };
  168. let tricky = placingOptions.room.getAnyDirection(trickier);
  169. if (tricky != undefined) {
  170. placingOptions.room.mapRoom(connectableRoom, tricky.trickyRoomDirection);
  171. placingOptions.map.map(placingOptions.room, tricky.x, tricky.y);
  172. return true;
  173. }
  174. }
  175. // THIS WORKS
  176. // I DON'T KNOW WHY
  177. // I DON'T CARE WHY
  178. // LEAVE
  179. let connectableThroughFodder = (fodderStep : number, connectingRoom : RoomRandom) => {
  180. let trickier = <TrickierOptions> {
  181. region : placingOptions.region,
  182. map : placingOptions.map,
  183. otherRoom : connectingRoom
  184. };
  185. if (fodderStep == 0) {
  186. return placingOptions.room.getAnyDirection(trickier);
  187. } else {
  188. let newFodder = new (placingOptions.region.fodderRoomClass)();
  189. //let directionShuffler = new Shuffler<number>(newFodder.connectableOn, RegionRandom.rng);
  190. let preferredGrowthDirection = placingOptions.map.getPreferredGrowth();
  191. let directionShuffler = new ShufflerDirection(placingOptions.room.connectableOn, preferredGrowthDirection);
  192. for (let direction = directionShuffler.getDirection(); direction != undefined; direction = directionShuffler.getDirection()) {
  193. let oppositeDirection = OppositeDirection[Direction[direction]];
  194. let otherCoordinates = placingOptions.map.getCoordinates(connectingRoom);
  195. let wouldbeCoordinates = Room.shift(otherCoordinates, oppositeDirection);
  196. let fodderTricky = {
  197. otherRoom : connectingRoom,
  198. otherRoomDirection : oppositeDirection,
  199. trickyRoomDirection : direction,
  200. map : placingOptions.map,
  201. region : placingOptions.region,
  202. x : wouldbeCoordinates[0],
  203. y : wouldbeCoordinates[1]
  204. };
  205. if (newFodder.isPlaceable(fodderTricky)) {
  206. newFodder.mapRoom(connectingRoom, fodderTricky.trickyRoomDirection);
  207. placingOptions.map.map(newFodder, fodderTricky.x, fodderTricky.y);
  208. let nextTricky = connectableThroughFodder(fodderStep - 1, newFodder);
  209. if (nextTricky != undefined) {
  210. placingOptions.region.place(newFodder);
  211. return nextTricky;
  212. } else {
  213. newFodder.unmapRoom(fodderTricky.trickyRoomDirection);
  214. placingOptions.map.unmap(fodderTricky.x, fodderTricky.y);
  215. }
  216. }
  217. }
  218. }
  219. };
  220. for (let fodderLevel = 1; fodderLevel < 40; fodderLevel++) {
  221. roomShuffler.restart();
  222. for (let connectableRoom = roomShuffler.getOne(); connectableRoom != undefined; connectableRoom = roomShuffler.getOne()) {
  223. let tricky = connectableThroughFodder(fodderLevel, connectableRoom);
  224. if (tricky != undefined) {
  225. placingOptions.room.mapRoom(tricky.otherRoom, tricky.trickyRoomDirection);
  226. placingOptions.map.map(placingOptions.room, tricky.x, tricky.y);
  227. return true;
  228. }
  229. }
  230. }
  231. Elements.CurrentTurnHandler.printAsError("Unable to place room " + placingOptions.room.getPrintedName() + ": All attempts failed");
  232. return false;
  233. }
  234. });
  235. }