RoomRandom.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. /// <reference path="../Room.ts" />
  2. interface TrickierOptions {
  3. region : RegionRandom;
  4. map : RoomRandomMap;
  5. otherRoom : RoomRandom;
  6. }
  7. interface TrickyOptions extends TrickierOptions {
  8. otherRoomDirection : number;
  9. trickyRoomDirection : number;
  10. x : number;
  11. y : number;
  12. }
  13. interface RoomRandomNode {
  14. room : RoomRandom;
  15. coordinates : Array<number>;
  16. distance : number;
  17. }
  18. class RoomRandom extends Room {
  19. public connectableOn : Array<number> = [Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST];
  20. public randomizable = true; // non-randomizable rooms don't get placed automatically
  21. public placed = false;
  22. public appearChance = 75;
  23. public extraConnectionChance = 15;
  24. public backgroundImage = "tomato";
  25. public lastMap : RoomRandomMap;
  26. public constructor (id? : string, fodder? : boolean, forbidden? : boolean) {
  27. super(id, fodder);
  28. this.randomizable = forbidden != true;
  29. }
  30. public getBackgroundClass () {
  31. if (this.isImageDefined()) {
  32. return this.backgroundImage;
  33. }
  34. return "tomato";
  35. }
  36. private isImageDefined () {
  37. try {
  38. for (var i = 0; i < document.styleSheets.length; i++) {
  39. var rules = document.styleSheets[i]['rules'] || document.styleSheets[i]['cssRules'];
  40. for (var x in rules) {
  41. if (typeof rules[x].selectorText == 'string' && rules[x].selectorText == "." + this.backgroundImage) {
  42. return true;
  43. }
  44. }
  45. }
  46. console.warn("Room background " + this.backgroundImage + " not found.");
  47. return false;
  48. } catch (e) {
  49. console.warn("Unable to read image");
  50. return true;
  51. }
  52. }
  53. public isConnectableOn (oppositeDirection : number) {
  54. return this.connectableOn.indexOf(oppositeDirection) != -1;
  55. }
  56. public trickyCode : (options : TrickyOptions) => boolean;
  57. public getAnyDirection (options : TrickierOptions) : TrickyOptions {
  58. let directionShuffler = new Shuffler<number>(this.connectableOn);
  59. for (let direction = directionShuffler.getOne(); direction != undefined; direction = directionShuffler.getOne()) {
  60. let oppositeDirection = OppositeDirection[Direction[direction]];
  61. let otherCoordinates = options.map.getCoordinates(options.otherRoom);
  62. let wouldbeCoordinates = Room.shift(otherCoordinates, oppositeDirection);
  63. let trickyOptions = {
  64. otherRoom : options.otherRoom,
  65. otherRoomDirection : oppositeDirection,
  66. trickyRoomDirection : direction,
  67. map : options.map,
  68. region : options.region,
  69. x : wouldbeCoordinates[0],
  70. y : wouldbeCoordinates[1]
  71. };
  72. if (this.isPlaceable(trickyOptions)) {
  73. return trickyOptions;
  74. }
  75. }
  76. }
  77. public isPlaceable (options : TrickyOptions) {
  78. if (!this.isConnectableOn(options.trickyRoomDirection) || !options.map.isFree(options.x, options.y)) {
  79. // This can't connect through that!
  80. // That coordinate isn't free!
  81. return false;
  82. }
  83. if (options.otherRoom == undefined || !options.otherRoom.isConnectableOn(options.otherRoomDirection)) {
  84. // There is no other room there?
  85. // The other room doesn't like it this way
  86. return false;
  87. }
  88. // Do I have my own tricky code?
  89. if (this.trickyCode != undefined) {
  90. return this.trickyCode(options);
  91. }
  92. return true;
  93. }
  94. public getDistanceTo (room : RoomRandom) {
  95. let otherCoordinates = this.lastMap.getCoordinates(room);
  96. return this.getDistanceToCoordinates(otherCoordinates);
  97. }
  98. public getDistanceToCoordinates (otherCoordinates) {
  99. let myCoordinates = this.lastMap.getCoordinates(this);
  100. if (myCoordinates != undefined && otherCoordinates != undefined) {
  101. let c1 = myCoordinates;
  102. let c2 = otherCoordinates;
  103. return Math.abs(c1[0] - c2[0]) + Math.abs(c1[1] - c2[1]);
  104. }
  105. }
  106. /**
  107. * Returns the closest direction we have in North, South, etc.
  108. * @param coordinates
  109. */
  110. public getOverallDirectionTo (coordinates) {
  111. let myCoordinates = this.lastMap.getCoordinates(this);
  112. let dy = (coordinates[1] - myCoordinates[1]);
  113. let dx = (coordinates[0] - myCoordinates[0]);
  114. let theta = Math.atan2(dy, dx); // range (-PI, PI]
  115. theta *= 180 / Math.PI; // rads to degs, range (-180, 180];
  116. theta = Math.round(theta / 45);
  117. if (theta < 0) {
  118. theta = 8 + theta;
  119. }
  120. let directions = [Direction.EAST, Direction.NORTHEAST, Direction.NORTH, Direction.NORTHWEST, Direction.WEST, Direction.SOUTHWEST, Direction.SOUTH, Direction.SOUTHEAST];
  121. return directions[theta];
  122. //if (theta < 0) theta = 360 + theta; // range [0, 360)
  123. }
  124. /**
  125. * This implementation is sufficiently fast for constant use.
  126. * @param pathEnd
  127. * @param map
  128. * @param availableRooms
  129. * @returns {Array}
  130. */
  131. public findPathTo (pathEnd : RoomRandom, validRoom? : (room : RoomRandom) => boolean) {
  132. validRoom = validRoom == undefined ? () => {return true;} : validRoom;
  133. let map = this.lastMap;
  134. let endPosition = map.getCoordinates(pathEnd);
  135. let open = [];
  136. let distance = (c1, c2) => {
  137. return Math.abs(c1[0] - c2[0]) + Math.abs(c1[1] - c2[1]);
  138. };
  139. let neighbors = (room : RoomRandom, x : number, y : number) => {
  140. let neighs = [];
  141. for (let direction = 0; direction < room.connections.length; direction++) {
  142. let otherRoom = room.connections[direction];
  143. if (otherRoom != undefined && open.indexOf(otherRoom) == -1 && validRoom(<RoomRandom> otherRoom)) {
  144. let dirCoordinates = Room.shift([x, y], direction);
  145. let dir = [otherRoom, dirCoordinates, distance(endPosition, dirCoordinates)];
  146. neighs.push(dir);
  147. }
  148. }
  149. return neighs.sort((a,b) => { return (<number> a[2]) - (<number> b[2]);});
  150. };
  151. let shortestPath = {
  152. length : map.getRoomCount()
  153. };
  154. let noPath = shortestPath;
  155. let cPath = [];
  156. let findPath = (myArray) => {
  157. let room = myArray[0];
  158. cPath.push(myArray);
  159. if (room == pathEnd) {
  160. if (shortestPath.length >= cPath.length) {
  161. shortestPath = cPath.slice(0);
  162. }
  163. } else if (shortestPath.length > (cPath.length)) {
  164. open.push(room);
  165. let otherRooms = neighbors(room, myArray[1][0], myArray[1][1]);
  166. for (let i = 0; i < otherRooms.length; i++) {
  167. if ((cPath.length + 1) < shortestPath.length) {
  168. findPath(otherRooms[i]);
  169. }
  170. }
  171. open.pop();
  172. }
  173. cPath.pop();
  174. };
  175. findPath([this, map.getCoordinates(this)]);
  176. return shortestPath != noPath ? shortestPath : undefined;
  177. }
  178. public getBestDirectionTo (otherRoom : RoomRandom, validRoom? : (room : RoomRandom) => boolean) {
  179. let path = this.findPathTo(otherRoom, validRoom);
  180. if (path != undefined) {
  181. if (path.length == 1) {
  182. return undefined;
  183. }
  184. return this.connections.indexOf(path[1][0]);
  185. }
  186. }
  187. public getAStarPathTo (otherRoom : RoomRandom, validRoom? : (room : RoomRandom) => boolean) {
  188. validRoom = validRoom != undefined ? validRoom : () => {return true};
  189. let distance = (c1, c2) => {
  190. return Math.abs(c1[0] - c2[0]) + Math.abs(c1[1] - c2[1]);
  191. };
  192. let isVisited = (room) => {
  193. return visited.indexOf(room) != -1;
  194. };
  195. let getNeighbors = (node : RoomRandomNode) => {
  196. let neighbors : Array<RoomRandomNode> = [];
  197. for (let direction = 0; direction < node.room.connections.length; direction++) {
  198. if (node.room.connections[direction] != undefined && !isVisited(node.room.connections[direction]) && validRoom(<RoomRandom> node.room.connections[direction])) {
  199. let coordinates = Room.shift(node.coordinates, direction);
  200. neighbors.push({
  201. room : <RoomRandom> node.room.connections[direction],
  202. coordinates : coordinates,
  203. distance : distance(coordinates, endNode.coordinates)
  204. });
  205. visited.push(<RoomRandom> node.room.connections[direction]);
  206. }
  207. }
  208. return neighbors;
  209. };
  210. let getClosestPath = () => {
  211. let shortest = 0;
  212. for (let i = 1; i < open.length; i++) {
  213. let lastPoint = open[i][open[i].length - 1];
  214. if (lastPoint.distance < open[shortest][open[shortest].length - 1].distance) {
  215. shortest = i;
  216. }
  217. }
  218. return shortest;
  219. };
  220. let endNode = {room : otherRoom, coordinates : this.lastMap.getCoordinates(otherRoom), distance : 0};
  221. let startCoordinates = this.lastMap.getCoordinates(this);
  222. let startNode = {room : this, coordinates : startCoordinates, distance : distance(startCoordinates, endNode.coordinates)};
  223. let open : Array<Array<RoomRandomNode>> = [[startNode]];
  224. let closed : Array<Array<RoomRandomNode>> = [];
  225. let shortestPath = this.lastMap.getRoomCount();
  226. let shortestIndex;
  227. let myPath;
  228. let closest = 0;
  229. let visited : Array<RoomRandom> = [this];
  230. while (open.length > 0) {
  231. myPath = open.splice(closest, 1)[0];
  232. if (myPath[myPath.length - 1].distance == 0) {
  233. let push = closed.push(myPath);
  234. if (myPath.length < shortestPath) {
  235. shortestPath = myPath.length;
  236. shortestIndex = push - 1;
  237. }
  238. // lazy, first path is very likely to be the best
  239. break;
  240. } else {
  241. let neighbors = getNeighbors(myPath[myPath.length - 1]);
  242. for (let i = 0; i < neighbors.length; i++) {
  243. open.push(myPath.concat([neighbors[i]]));
  244. }
  245. }
  246. for (let i = open.length - 1; i >= 0; i--) {
  247. if (open[i].length >= shortestPath) {
  248. open.splice(i, 1);
  249. }
  250. }
  251. closest = getClosestPath();
  252. }
  253. return closed[shortestIndex];
  254. }
  255. public getAStarBestDirectionTo (otherRoom : RoomRandom, validRoom? : (room : RoomRandom) => boolean) {
  256. let path = this.getAStarPathTo(otherRoom, validRoom);
  257. if (path != undefined) {
  258. if (path.length == 1) {
  259. return undefined;
  260. }
  261. return this.connections.indexOf(path[1].room);
  262. }
  263. }
  264. public getConnectedDirection () {
  265. let shuffler = new Shuffler(Room.DIRECTIONS);
  266. for (let direction = shuffler.getOne(); direction != undefined; direction = shuffler.getOne()) {
  267. if (this.connections[direction] != undefined) {
  268. return direction;
  269. }
  270. }
  271. }
  272. // TODO: Return all things of type that are in placed RoomRandom.
  273. public static getActive (type : typeof Thing) {
  274. }
  275. }
  276. // random = new RoomRandom...
  277. // random.connectableOn = [Room.DIRECTION_NORTH, Room.DIRECTION_SOUTH...]