Location.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. class GameLocation{
  2. _isPublic = false; get isPublic(){return this._isPublic;}
  3. _isOutdoors = false; get isOutdoors(){return this._isOutdoors;}
  4. _isHome = false; get isHome(){return this._isHome;}
  5. _region = ''; get region(){return this._region;}
  6. _passage = '';
  7. _passageArguments = []; get passageArguments(){return this._passageArguments;}
  8. _passageQueue = [];
  9. _passageQueueMaxLength = 3;
  10. get locationIdentifier():LocationIdentifier{
  11. return new LocationIdentifier(this.passage,this.passageArguments);
  12. }
  13. set locationIdentifier(v:LocationIdentifier){
  14. this.update(v.passage,v.passageArguments);
  15. }
  16. get passage(){return this._passage;}
  17. set passage(v:string){
  18. if(typeof v != "string")
  19. {
  20. console.error('Location set to invalid passage:',v);
  21. return;
  22. }
  23. if(!Story.has(v)){
  24. console.error('Location set to non-existing passage:',v);
  25. return;
  26. }
  27. this._passageQueue.push(this._passage);
  28. while(this._passageQueue.length > this._passageQueueMaxLength) this._passageQueue.shift();
  29. this._passage = v;
  30. let passage_tags = tags(v);
  31. if(passage_tags.includes('indoors'))
  32. this._isOutdoors = false;
  33. else if(passage_tags.includes('outdoors'))
  34. this._isOutdoors = true;
  35. else
  36. console.warn("Location-passage does not contain an indoors- or outdoors-tag:",v);
  37. if(passage_tags.includes('private'))
  38. this._isPublic = false;
  39. else if(passage_tags.includes('public'))
  40. this._isPublic = true;
  41. else
  42. console.warn("Location-passage does not contain a private- or public-tag:",v);
  43. let homeTag = State.variables.housing.home;
  44. this._isHome = passage_tags.includes(homeTag);
  45. for(let tag of passage_tags){
  46. if(tag.startsWith('region_')){
  47. this._region = tag.replace('region_','');
  48. break;
  49. }
  50. }
  51. }
  52. /**
  53. * A unique identifier for the place the player is at.
  54. * @readonly
  55. * @type {string}
  56. */
  57. get here(){
  58. return this.passage;
  59. }
  60. get hasWardrobeAccess(){
  61. return this.isHome || this.tags.includes('wardrobe');
  62. }
  63. get tags(){
  64. return tags(this.passage);
  65. }
  66. #constantLocationData(locationId:string):LocationDefinition{
  67. return setup.getLocation(locationId);
  68. }
  69. _locationValues = {};
  70. get<T extends any>(locationId:string,field:string,def:T):T{
  71. const constData = this.#constantLocationData(locationId);
  72. const dynamicData = this._locationValues[locationId] ?? {};
  73. const combinedData = Object.assign({},constData,dynamicData);
  74. return combinedData[field] ?? def;
  75. }
  76. getHere(field:string,def:any=false){
  77. return this.get(this.here,field,def);
  78. }
  79. inc(locationId:string,field:string,v=1){
  80. this.set(locationId,field,this.get(locationId,field,0)+v);
  81. }
  82. set(locationId:string,field:string,v:any){
  83. this._locationValues[locationId] ??= {};
  84. this._locationValues[locationId][field] = v;
  85. }
  86. setHere(field:string,v:any){
  87. this.set(this.here,field,v);
  88. }
  89. /**
  90. * Updates the current location and passage arguments.
  91. * @param {string} locationId
  92. * @param {Array<any>} [locationArguments=[]]
  93. */
  94. update(locationId:string,locationArguments:Array<any>=[]){
  95. this.passage = locationId;
  96. this._passageArguments = locationArguments;
  97. }
  98. //#region Furniture
  99. get furniture(){
  100. return this.getHere('furniture',{});
  101. }
  102. hasFurniture(furnitureId:string){
  103. return (furnitureId in this.furniture);
  104. }
  105. removeFurniture(furnitureId:string){
  106. let furniture = this.furniture;
  107. delete furniture[furnitureId];
  108. this.setHere('furniture',furniture);
  109. }
  110. updateFurniture(furnitureId:string,updateObject:object){
  111. let furniture = this.furniture;
  112. furniture[furnitureId] = setup.mergeDeep(furniture[furnitureId] ?? {},updateObject);
  113. this.setHere('furniture',furniture);
  114. }
  115. //#endregion
  116. //#region OpenTime
  117. isOpen(locationId: string=undefined){
  118. locationId ??= this.passage;
  119. const openTimes:TimespanIdentifier = this.get(locationId,'openTimes',{});
  120. return State.variables.time.timespanIdentifierMatches(openTimes);
  121. }
  122. openingTimesStr(locationId: string=undefined){
  123. locationId ??= this.passage;
  124. const openTimes:TimespanIdentifier = this.get(locationId,'openTimes',{});
  125. return State.variables.time.timespanIdentifierToString(openTimes);
  126. }
  127. //#endregion
  128. //#region TagValues
  129. _tagValues={};
  130. getTagValue(tag:string,field:string,def:any=undefined){
  131. return (this._tagValues[tag]?.[field] ?? def);
  132. }
  133. getTagsValues(tags:string[],field:string,def:any=undefined):any[]{
  134. let result = [];
  135. for(const tag of tags){
  136. result.push(this.getTagValue(tag,field,def));
  137. }
  138. return result;
  139. }
  140. getTagsValuesHere(field:string,def:any=undefined):any[]{
  141. return this.getTagsValues(this.tags,field,def);
  142. }
  143. /*getTagsValuesAccumulated(tags:string[],field:string,acummulation:string):any{
  144. switch (acummulation) {
  145. case '||':
  146. let val_or = this.getTagsValues(tags,field,false);
  147. return val_or.includes(true);
  148. case '&&':
  149. let val_and = this.getTagsValues(tags,field,false);
  150. return !val_and.includes(false);
  151. case 'latest':
  152. const startdate = new Date(0);
  153. let val_latest:Date[] = this.getTagsValues(tags,field,startdate);
  154. return val_latest.reduce(
  155. (previous,current)=>new Date(
  156. Math.max(
  157. previous.getTime(),
  158. current.getTime()
  159. )
  160. )
  161. ,startdate);
  162. default:
  163. break;
  164. }
  165. }*/
  166. setTagValue(tag:string,field:string,tagValue:any){
  167. this._tagValues[tag] ??= {};
  168. this._tagValues[tag][field] = tagValue;
  169. }
  170. //#endregion
  171. //#region System
  172. _init(location){
  173. Object.keys(location).forEach(function (pn) {
  174. this[pn] = clone(location[pn]);
  175. }, this);
  176. return this;
  177. }
  178. clone = function () {
  179. return (new setup.Location())._init(this);
  180. };
  181. toJSON = function () {
  182. var ownData = {};
  183. Object.keys(this).forEach(function (pn) {
  184. if(typeof this[pn] !== "function")
  185. ownData[pn] = clone(this[pn]);
  186. }, this);
  187. return JSON.reviveWrapper('(new setup.Location())._init($ReviveData$)', ownData);
  188. };
  189. //#endregion
  190. }
  191. setup.Location = GameLocation;