123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- /// <reference path="../../Elements/Classes/Say.ts" />
- /// <reference path="Relations/RelationOneToMany.ts" />
- /// <reference path="../../Elements/Classes/Say/SayImage.ts" />
- interface ThingOptions {
- name? : string;
- properName? : string;
- description? : Say | any;
- unique? : boolean;
- image? : SayImage | string;
- shiny? : boolean;
- breakable? : boolean;
- resistance? : number;
- breaksTo? : string;
- }
- // A thing is something that exists in the World
- class Thing implements Printable {
- protected name : string;
- public properlyNamed : boolean = false;
- public scenery : boolean = false;
- public fixedInPlace : boolean = false;
- public animated : boolean = false;
- public visible : boolean = true;
- public unique : boolean = false;
- public image : SayImage;
- protected shiny : boolean = false;
- public breakable = false;
- public breakableOn = 5;
- public breaksTo;
- protected setAlterations : Array<(thisObject : Thing, simpleAlterationObject : {[id : string] : any}) => void> = [];
- protected getAlterations : Array<(thisObject : Thing) => {[id : string] : any}> = [];
- public clone : (includeChanges? : boolean) => Thing = function () {
- throw new Error("Non-unique Objects can't be cloned.");
- };
- public cloneOptions : ThingOptions;
- public description : Say | string | (() => Say | string);
- public static uniqueThings : {[id : string] : Thing} = {};
- public static things : {[id : string] : Array<Thing>} = {};
- // This is not safe.
- // public duplicate () {
- // return new (<any> this.constructor)(<ThingOptions> {
- // name : this.name,
- // properName : this.properlyNamed ? this.name : undefined,
- // description : this.description,
- // unique : this.unique,
- // image : this.image
- // });
- // }
- public constructor (options? : ThingOptions) {
- options = options == undefined ? {} : options;
- this.breakable = options.breakable === true;
- this.breakableOn = options.resistance == undefined ? 5 : options.resistance;
- this.breaksTo = options.breaksTo;
- if (options.properName != undefined) {
- this.name = options.properName;
- this.properlyNamed = true;
- } else if (options.name != undefined) {
- this.name = options.name;
- } else {
- this.name = (<any> this.constructor).name; // If there is no name, use Class as name
- }
- if (options.description != undefined) {
- if (options.description instanceof Say) {
- this.description = options.description;
- } else {
- this.description = new Say(options.description);
- }
- }
- if (options.unique) {
- Thing.storeUnique(this);
- this.unique = true;
- } else {
- Thing.storeNonUnique(this);
- this.cloneOptions = options;
- this.clone = (includeChanges? : boolean) => {
- let cons = <any> eval((<any> this.constructor).name);
- let newThing = <Thing> new cons(this.cloneOptions);
- if (includeChanges == undefined || includeChanges) {
- newThing.setChanges(this.getChanges());
- }
- return newThing;
- };
- }
- if (options.image != undefined) {
- if (options.image instanceof SayImage) {
- this.image = options.image;
- } else {
- this.image = new SayImage(options.image);
- }
- }
- this.shiny = options.shiny == true;
- this.addGetAlterations((thing : Thing) => {
- function getClosestRoom (currentRoom : RoomRandom, rooms : Array<RoomRandom>) {
- if (currentRoom instanceof RoomRandom && rooms.length > 0) {
- rooms.sort((a : RoomRandom, b : RoomRandom) => {
- if (!(a instanceof RoomRandom)) return -1;
- if (!(b instanceof RoomRandom)) return 1;
- let da = a.getDistanceTo(<RoomRandom> currentRoom);
- let db = b.getDistanceTo(<RoomRandom> currentRoom);
- return da - db;
- });
- return {
- Location : rooms[0].getName()
- }
- }
- }
- if (Thing.EnclosedRelation.getLeft(thing) == thing.getRoom() && thing.getRoom() != undefined) {
- if (thing.getRoom().fodder) {
- if (thing.isPlayer()) {
- // put at closest remembered room
- let rooms = WorldState.getRememberedRoomsAsRooms();
- let currentRoom = thing.getRoom();
- return getClosestRoom(<RoomRandom> currentRoom, <Array<RoomRandom>> rooms);
- } else {
- // put at closest room
- let rooms = thing.getRoom().getConnectedRooms();
- let currentRoom = thing.getRoom();
- let foundRoom = getClosestRoom(<RoomRandom> currentRoom, <Array<RoomRandom>> rooms);
- if (foundRoom != undefined) {
- return foundRoom;
- } else {
- rooms = (<Region> Region.InRelation.getLeft(thing.getRoom())).getRooms();
- return getClosestRoom(<RoomRandom> currentRoom, <Array<RoomRandom>> rooms);
- }
- }
- } else {
- return {
- Location: thing.getRoom().getName()
- }
- }
- }
- });
- this.addSetAlterations((thing : Thing, changes) => {
- if (changes.Location != undefined) {
- let room = Room.getRoom(changes.Location);
- if (room != undefined) {
- room.place(thing);
- } else {
- console.error("Unable to place ", thing, " at room ", changes.Location);
- }
- }
- });
- }
- public addGetAlterations (newGet) {
- this.getAlterations.push(newGet);
- }
- /**
- * This adds a function to run over when loading from a save file.
- * Always remember that save files are NOT SAFE. Ideally we should check for invalid information,
- * but at least check if they exist, because if you added something new, old saves will not have them.
- * It's okay to break on bad information because if someone decided to hack their save, them they should
- * deal with the issues.
- * @param newSet
- */
- public addSetAlterations (newSet) {
- this.setAlterations.push(newSet);
- }
- public getChanges () : {[id : string] : any}{
- let changes = {};
- for (let i = 0; i < this.getAlterations.length; i++) {
- let change = this.getAlterations[i](this);
- for (let key in change) {
- changes[key] = change[key];
- }
- }
- return changes;
- }
- public setChanges (simpleAlterationObject : {[id : string] : any}) {
- for (let i = 0; i < this.setAlterations.length; i++) {
- this.setAlterations[i](this, simpleAlterationObject);
- }
- }
- public getShiny () {
- return this.shiny;
- }
- public setName (name : string) {
- this.name = name; // Don't restore. The only thing that changes names is player.
- }
- public getName () {
- return this.name;
- }
- public static storeNonUnique (thing : Thing) {
- if (Thing.things[thing.name] == undefined) {
- Thing.things[thing.name] = [thing];
- } else {
- Thing.things[thing.name].push(thing);
- }
- }
- public static getNonUnique (name : string) : Array<Thing> {
- return Thing.things[name] == undefined ? [] : Thing.things[name];
- }
- public static getOneThing (name : string) {
- let thing : Thing = Thing.getUnique(name);
- if (thing == undefined) {
- let things = Thing.getNonUnique(name);
- if (things.length > 0) {
- thing = things[0];
- }
- }
- return thing;
- }
- public static storeUnique (unique : Thing) {
- if (Thing.uniqueThings[unique.name] != undefined) {
- console.warn(unique.name, Thing.uniqueThings[unique.name], new Error("Unique Thing Already Exists"));
- } else {
- Thing.uniqueThings[unique.name] = unique;
- }
- }
- public static getUnique (name : string) {
- return Thing.uniqueThings[name];
- }
- public static getUniques () : Array<Thing> {
- let things = [];
- for (let name in Thing.uniqueThings) {
- things.push(Thing.uniqueThings[name]);
- }
- return things;
- }
- public getPrintedName() {
- return this.name;
- }
- public getPrintedDescription () {
- if (this.description == undefined) {
- return new Say ("You see nothing special about ", new SayThe(), this, ".");
- } else {
- return this.description;
- }
- }
- public static InsideRoomRelation : RelationOneToMany= new RelationOneToMany();
- public static PartRelation = new RelationOneToMany();
- public static CarryRelation = new RelationOneToMany();
- public static WieldRelation = new RelationOneToMany();
- public static WearRelation = new RelationOneToMany();
- public static EnclosedRelation = new RelationHandlerStrictOneToMany(Thing.InsideRoomRelation, Thing.PartRelation, Thing.CarryRelation, Thing.WieldRelation, Thing.WearRelation);
- public getPartOne () {
- return Thing.PartRelation.getLeft(this);
- }
- public getCarryOne () {
- return Thing.CarryRelation.getLeft(this);
- }
- public getWieldOne () {
- return Thing.WieldRelation.getLeft(this);
- }
- public getWearOne () {
- return Thing.WearRelation.getLeft(this);
- }
- public getEnclosedOne () : Thing | Room {
- return Thing.EnclosedRelation.getLeft(this);
- }
- public removeParts (partType? : any) {
- let parts = this.getParts(partType);
- for (let i = 0; i < parts.length; i++) {
- Thing.PartRelation.unsetRight(parts[i]);
- }
- }
- public getWorns (wornType? : any) {
- if (wornType != undefined) {
- return Thing.WearRelation.getRightType(this, wornType);
- }
- return Thing.WearRelation.getRight(this);
- }
- public getParts (partType? : any) {
- if (partType != undefined) {
- return Thing.PartRelation.getRightType(this, partType);
- }
- return Thing.PartRelation.getRight(this);
- }
- public getPartsByName (name : string) : Array<Thing> {
- let parts = this.getParts();
- return parts.filter((part : Thing) => {
- return (part.getName() == name);
- });
- }
- public getPart (partType? : any) {
- if (partType != undefined) {
- return Thing.PartRelation.getRightTypeOne(this, partType);
- }
- return Thing.PartRelation.getRight(this);
- }
- // Will usually return the room at which this is.
- // Will return the highest parent if that parent is out of world.
- public getHighestEnclosedOne () : Thing | Room {
- return Thing.EnclosedRelation.getLastLeft(this);
- }
- public getHighestEnclosedOneNotRoom () : Thing {
- let parent = Thing.EnclosedRelation.getLeft(this);
- if (parent != undefined) {
- let newParent = Thing.EnclosedRelation.getLeft(parent);
- while (newParent != undefined) {
- parent = newParent;
- newParent = Thing.EnclosedRelation.getLeft(parent);
- }
- return parent;
- }
- return this;
- }
- public getRoom () : Room {
- var partOf = <Thing | Room> Thing.EnclosedRelation.getLeft(this);
- if (partOf instanceof Room) {
- return partOf;
- } else if (partOf instanceof Thing) {
- return partOf.getRoom();
- }
- }
- public removeFromRoom () {
- this.getRoom().remove(this);
- }
- public isVisibleTo (thing : Thing) {
- // Either in the same room or part of /wielded / carried by person
- return (this.getRoom() == thing.getRoom() && this.visible);
- // TODO : Update this code for the cases where a creature is not visible but the observer can see them anyway
- }
- public isPlayer () {
- return false;
- }
- public addParts (...parts : Array<Thing>) {
- parts.forEach(part => {
- Thing.PartRelation.setRelation(this, part);
- });
- }
- public isUnique () {
- return this.unique;
- }
- public setCarried (thing : Thing) {
- Thing.CarryRelation.setRelation(this, thing);
- }
- public setWorn (thing : Thing) {
- Thing.WearRelation.setRelation(this, thing);
- }
- public setWielded (thing : Thing) {
- Thing.WieldRelation.setRelation(this, thing);
- }
- public unsetCarried (thing : Thing) {
- if (Thing.EnclosedRelation.getAllRight(this).indexOf(thing) != -1) {
- Thing.EnclosedRelation.unsetRight(thing);
- this.getRoom().place(thing);
- }
- }
- public break () {
- if (this.breaksTo != undefined) {
- let unique = Thing.getUnique(this.breaksTo);
- if (unique != undefined) {
- this.getRoom().place(unique);
- } else {
- let nonunique = Thing.getOneThing(this.breaksTo);
- if (nonunique != undefined) {
- this.getRoom().place(nonunique.clone())
- }
- }
- }
- this.destroy();
- }
- /**
- * So long as a thing is in a relation, it can't be garbage cleaned.
- * If you're throwing a thing away, do run destroy.
- * Warning: this will also destroy everything that's below it.
- */
- public destroy () {
- let relatedRight = Thing.EnclosedRelation.getAllRight(this);
- relatedRight.push(this);
- relatedRight.forEach(related => {
- Thing.EnclosedRelation.unset(related);
- });
- }
- }
|