1
1

Thing.ts 14 KB

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