Thing.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. /// <reference path="../../Elements/Classes/Say.ts" />
  2. /// <reference path="Relations/RelationOneToMany.ts" />
  3. /// <reference path="../../Elements/Classes/Say/SayImage.ts" />
  4. interface ThingOptions {
  5. name? : string;
  6. properName? : string;
  7. description? : Say | any;
  8. unique? : boolean;
  9. image? : SayImage | string;
  10. shiny? : boolean;
  11. }
  12. // A thing is something that exists in the World
  13. class Thing implements Printable {
  14. protected name : string;
  15. public properlyNamed : boolean = false;
  16. public scenery : boolean = false;
  17. public fixedInPlace : boolean = false;
  18. public animated : boolean = false;
  19. public visible : boolean = true;
  20. public unique : boolean = false;
  21. public image : SayImage;
  22. protected shiny : boolean = false;
  23. protected setAlterations : Array<(thisObject : Thing, simpleAlterationObject : {[id : string] : any}) => void> = [];
  24. protected getAlterations : Array<(thisObject : Thing) => {[id : string] : any}> = [];
  25. public clone : (includeChanges? : boolean) => Thing = function () {
  26. throw new Error("Non-unique Objects can't be cloned.");
  27. };
  28. public cloneOptions : ThingOptions;
  29. public description : Say | string | (() => Say | string);
  30. public static uniqueThings : {[id : string] : Thing} = {};
  31. public static things : {[id : string] : Array<Thing>} = {};
  32. // This is not safe.
  33. // public duplicate () {
  34. // return new (<any> this.constructor)(<ThingOptions> {
  35. // name : this.name,
  36. // properName : this.properlyNamed ? this.name : undefined,
  37. // description : this.description,
  38. // unique : this.unique,
  39. // image : this.image
  40. // });
  41. // }
  42. public constructor (options? : ThingOptions) {
  43. options = options == undefined ? {} : options;
  44. if (options.properName != undefined) {
  45. this.name = options.properName;
  46. this.properlyNamed = true;
  47. } else if (options.name != undefined) {
  48. this.name = options.name;
  49. } else {
  50. this.name = (<any> this.constructor).name; // If there is no name, use Class as name
  51. }
  52. if (options.description != undefined) {
  53. if (options.description instanceof Say) {
  54. this.description = options.description;
  55. } else {
  56. this.description = new Say(options.description);
  57. }
  58. }
  59. if (options.unique) {
  60. Thing.storeUnique(this);
  61. this.unique = true;
  62. } else {
  63. Thing.storeNonUnique(this);
  64. this.cloneOptions = options;
  65. this.clone = (includeChanges? : boolean) => {
  66. let cons = <any> eval((<any> this.constructor).name);
  67. let newThing = <Thing> new cons(this.cloneOptions);
  68. if (includeChanges == undefined || includeChanges) {
  69. newThing.setChanges(this.getChanges());
  70. }
  71. return newThing;
  72. };
  73. }
  74. if (options.image != undefined) {
  75. if (options.image instanceof SayImage) {
  76. this.image = options.image;
  77. } else {
  78. this.image = new SayImage(options.image);
  79. }
  80. }
  81. this.shiny = options.shiny == true;
  82. this.addGetAlterations((thing : Thing) => {
  83. function getClosestRoom (currentRoom : RoomRandom, rooms : Array<RoomRandom>) {
  84. if (currentRoom instanceof RoomRandom && rooms.length > 0) {
  85. rooms.sort((a : RoomRandom, b : RoomRandom) => {
  86. if (!(a instanceof RoomRandom)) return -1;
  87. if (!(b instanceof RoomRandom)) return 1;
  88. let da = a.getDistanceTo(<RoomRandom> currentRoom);
  89. let db = b.getDistanceTo(<RoomRandom> currentRoom);
  90. return da - db;
  91. });
  92. return {
  93. Location : rooms[0].getName()
  94. }
  95. }
  96. }
  97. if (Thing.EnclosedRelation.getLeft(thing) == thing.getRoom() && thing.getRoom() != undefined) {
  98. if (thing.getRoom().fodder) {
  99. if (thing.isPlayer()) {
  100. // put at closest remembered room
  101. let rooms = WorldState.getRememberedRoomsAsRooms();
  102. let currentRoom = thing.getRoom();
  103. return getClosestRoom(<RoomRandom> currentRoom, <Array<RoomRandom>> rooms);
  104. } else {
  105. // put at closest room
  106. let rooms = thing.getRoom().getConnectedRooms();
  107. let currentRoom = thing.getRoom();
  108. let foundRoom = getClosestRoom(<RoomRandom> currentRoom, <Array<RoomRandom>> rooms);
  109. if (foundRoom != undefined) {
  110. return foundRoom;
  111. } else {
  112. rooms = (<Region> Region.InRelation.getLeft(thing.getRoom())).getRooms();
  113. return getClosestRoom(<RoomRandom> currentRoom, <Array<RoomRandom>> rooms);
  114. }
  115. }
  116. } else {
  117. return {
  118. Location: thing.getRoom().getName()
  119. }
  120. }
  121. }
  122. });
  123. this.addSetAlterations((thing : Thing, changes) => {
  124. if (changes.Location != undefined) {
  125. let room = Room.getRoom(changes.Location);
  126. if (room != undefined) {
  127. room.place(thing);
  128. } else {
  129. console.error("Unable to place ", thing, " at room ", changes.Location);
  130. }
  131. }
  132. });
  133. }
  134. public addGetAlterations (newGet) {
  135. this.getAlterations.push(newGet);
  136. }
  137. /**
  138. * This adds a function to run over when loading from a save file.
  139. * Always remember that save files are NOT SAFE. Ideally we should check for invalid information,
  140. * but at least check if they exist, because if you added something new, old saves will not have them.
  141. * It's okay to break on bad information because if someone decided to hack their save, them they should
  142. * deal with the issues.
  143. * @param newSet
  144. */
  145. public addSetAlterations (newSet) {
  146. this.setAlterations.push(newSet);
  147. }
  148. public getChanges () : {[id : string] : any}{
  149. let changes = {};
  150. for (let i = 0; i < this.getAlterations.length; i++) {
  151. let change = this.getAlterations[i](this);
  152. for (let key in change) {
  153. changes[key] = change[key];
  154. }
  155. }
  156. return changes;
  157. }
  158. public setChanges (simpleAlterationObject : {[id : string] : any}) {
  159. for (let i = 0; i < this.setAlterations.length; i++) {
  160. this.setAlterations[i](this, simpleAlterationObject);
  161. }
  162. }
  163. public getShiny () {
  164. return this.shiny;
  165. }
  166. public setName (name : string) {
  167. this.name = name; // Don't restore. The only thing that changes names is player.
  168. }
  169. public getName () {
  170. return this.name;
  171. }
  172. public static storeNonUnique (thing : Thing) {
  173. if (Thing.things[thing.name] == undefined) {
  174. Thing.things[thing.name] = [thing];
  175. } else {
  176. Thing.things[thing.name].push(thing);
  177. }
  178. }
  179. public static getNonUnique (name : string) : Array<Thing> {
  180. return Thing.things[name] == undefined ? [] : Thing.things[name];
  181. }
  182. public static getOneThing (name : string) {
  183. let thing : Thing = Thing.getUnique(name);
  184. if (thing == undefined) {
  185. let things = Thing.getNonUnique(name);
  186. if (things.length > 0) {
  187. thing = things[0];
  188. }
  189. }
  190. return thing;
  191. }
  192. public static storeUnique (unique : Thing) {
  193. if (Thing.uniqueThings[unique.name] != undefined) {
  194. console.warn(unique.name, Thing.uniqueThings[unique.name], new Error("Unique Thing Already Exists"));
  195. } else {
  196. Thing.uniqueThings[unique.name] = unique;
  197. }
  198. }
  199. public static getUnique (name : string) {
  200. return Thing.uniqueThings[name];
  201. }
  202. public static getUniques () : Array<Thing> {
  203. let things = [];
  204. for (let name in Thing.uniqueThings) {
  205. things.push(Thing.uniqueThings[name]);
  206. }
  207. return things;
  208. }
  209. public getPrintedName() {
  210. return this.name;
  211. }
  212. public getPrintedDescription () {
  213. if (this.description == undefined) {
  214. return new Say ("You see nothing special about ", new SayThe(), this, ".");
  215. } else {
  216. return this.description;
  217. }
  218. }
  219. public static InsideRoomRelation : RelationOneToMany= new RelationOneToMany();
  220. public static PartRelation = new RelationOneToMany();
  221. public static CarryRelation = new RelationOneToMany();
  222. public static WieldRelation = new RelationOneToMany();
  223. public static WearRelation = new RelationOneToMany();
  224. public static EnclosedRelation = new RelationHandlerStrictOneToMany(Thing.InsideRoomRelation, Thing.PartRelation, Thing.CarryRelation, Thing.WieldRelation, Thing.WearRelation);
  225. public getPartOne () {
  226. return Thing.PartRelation.getLeft(this);
  227. }
  228. public getCarryOne () {
  229. return Thing.CarryRelation.getLeft(this);
  230. }
  231. public getWieldOne () {
  232. return Thing.WieldRelation.getLeft(this);
  233. }
  234. public getWearOne () {
  235. return Thing.WearRelation.getLeft(this);
  236. }
  237. public getEnclosedOne () : Thing | Room {
  238. return Thing.EnclosedRelation.getLeft(this);
  239. }
  240. public removeParts (partType? : any) {
  241. let parts = this.getParts(partType);
  242. for (let i = 0; i < parts.length; i++) {
  243. Thing.PartRelation.unsetRight(parts[i]);
  244. }
  245. }
  246. public getParts (partType? : any) {
  247. if (partType != undefined) {
  248. return Thing.PartRelation.getRightType(this, partType);
  249. }
  250. return Thing.PartRelation.getRight(this);
  251. }
  252. public getPartsByName (name : string) : Array<Thing> {
  253. let parts = this.getParts();
  254. return parts.filter((part : Thing) => {
  255. return (part.getName() == name);
  256. });
  257. }
  258. public getPart (partType? : any) {
  259. if (partType != undefined) {
  260. return Thing.PartRelation.getRightTypeOne(this, partType);
  261. }
  262. return Thing.PartRelation.getRight(this);
  263. }
  264. // Will usually return the room at which this is.
  265. // Will return the highest parent if that parent is out of world.
  266. public getHighestEnclosedOne () : Thing | Room {
  267. return Thing.EnclosedRelation.getLastLeft(this);
  268. }
  269. public getHighestEnclosedOneNotRoom () : Thing {
  270. let parent = Thing.EnclosedRelation.getLeft(this);
  271. if (parent != undefined) {
  272. let newParent = Thing.EnclosedRelation.getLeft(parent);
  273. while (newParent != undefined) {
  274. parent = newParent;
  275. newParent = Thing.EnclosedRelation.getLeft(parent);
  276. }
  277. return parent;
  278. }
  279. return this;
  280. }
  281. public getRoom () : Room {
  282. var partOf = <Thing | Room> Thing.EnclosedRelation.getLeft(this);
  283. if (partOf instanceof Room) {
  284. return partOf;
  285. } else if (partOf instanceof Thing) {
  286. return partOf.getRoom();
  287. }
  288. }
  289. public removeFromRoom () {
  290. this.getRoom().remove(this);
  291. }
  292. public isVisibleTo (thing : Thing) {
  293. // Either in the same room or part of /wielded / carried by person
  294. return (this.getRoom() == thing.getRoom() && this.visible);
  295. // TODO : Update this code for the cases where a creature is not visible but the observer can see them anyway
  296. }
  297. public isPlayer () {
  298. return false;
  299. }
  300. public addParts (...parts : Array<Thing>) {
  301. parts.forEach(part => {
  302. Thing.PartRelation.setRelation(this, part);
  303. });
  304. }
  305. public isUnique () {
  306. return this.unique;
  307. }
  308. public setCarried (thing : Thing) {
  309. Thing.CarryRelation.setRelation(this, thing);
  310. }
  311. public setWorn (thing : Thing) {
  312. Thing.WearRelation.setRelation(this, thing);
  313. }
  314. public setWielded (thing : Thing) {
  315. Thing.WieldRelation.setRelation(this, thing);
  316. }
  317. public unsetCarried (thing : Thing) {
  318. if (Thing.EnclosedRelation.getAllRight(this).indexOf(thing) != -1) {
  319. Thing.EnclosedRelation.unsetRight(thing);
  320. this.getRoom().place(thing);
  321. }
  322. }
  323. /**
  324. * So long as a thing is in a relation, it can't be garbage cleaned.
  325. * If you're throwing a thing away, do run destroy.
  326. * Warning: this will also destroy everything that's below it.
  327. */
  328. public destroy () {
  329. let relatedRight = Thing.EnclosedRelation.getAllRight(this);
  330. relatedRight.push(this);
  331. relatedRight.forEach(related => {
  332. Thing.EnclosedRelation.unset(related);
  333. });
  334. }
  335. }