wardrobe.ts 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113
  1. setup.wardrobeSettings = {
  2. bmiSizeMaxDifference: 2
  3. }
  4. setup.outfitsDefaults={
  5. clothes:{
  6. h:100,
  7. bmi:'$pc.bmi'
  8. }
  9. }
  10. setup.wardrobeWearableFunctions = {};
  11. class Wardrobe{
  12. _ownedItems:{[itemId:string]:OutfitOwnerShipData} = {}
  13. /**
  14. * Owned Item
  15. * @param {string} itemId
  16. */
  17. item(itemId:string):OutfitOwned{
  18. let item = OutfitItem.get(itemId) as OutfitItemClothes; // Trick Typescript
  19. let owneditem:OutfitOwned = Object.assign(item,{l:0,bmi:-2,h:-2},this._ownedItems[itemId]); // Item needs to be first, otherwise all its functions are broken
  20. return owneditem;
  21. }
  22. /**
  23. * Owned Items
  24. * @readonly
  25. * @type {{[key:string]:OutfitItem}}
  26. */
  27. get items():{[key:string]:OutfitOwned}{
  28. let result:{[key:string]:OutfitOwned} = {};
  29. for(const [itemId, itemData] of Object.entries(this._ownedItems)){
  30. //let item = OutfitItem.get(itemId);
  31. //Object.assign({l:0,bmi:-2,h:-2},item,itemData);
  32. result[itemId] = this.item(itemId);
  33. }
  34. return result;
  35. }
  36. itemsFiltered(filter:Filter){
  37. return setup.filterDict(this.items,filter);
  38. }
  39. //#region Worn Items
  40. _wornItems:WornOutfitDefinition = {
  41. bra: null,
  42. coat: null,
  43. clothes: null,
  44. panties: null,
  45. purse: null,
  46. shoes: null
  47. }
  48. get wornItemIds(){
  49. return this._wornItems;
  50. }
  51. get bra():OutfitItemBra{
  52. let braId = this.wornItemIds.bra;
  53. if(braId)
  54. return this.item(braId) as OutfitItemBra;
  55. return new OutfitItemBra();
  56. }
  57. get clothes():OutfitItemClothes|OutfitItemSwimwear{
  58. let clothesId = this.wornItemIds.clothes;
  59. if(clothesId)
  60. return this.item(clothesId) as OutfitItemClothes;
  61. return new OutfitItemClothes();
  62. }
  63. get coat():OutfitItemCoat{
  64. let coatId = this.wornItemIds.coat;
  65. if(coatId)
  66. return this.item(coatId) as OutfitItemCoat;
  67. return new OutfitItemCoat();
  68. }
  69. get panties():OutfitItemPanties{
  70. let pantiesId = this.wornItemIds.panties;
  71. if(pantiesId)
  72. return this.item(pantiesId) as OutfitItemPanties;
  73. return new OutfitItemPanties();
  74. }
  75. get purse():OutfitItemPurse{
  76. let purseId = this.wornItemIds.purse;
  77. if(purseId)
  78. return this.item(purseId) as OutfitItemPurse;
  79. return new OutfitItemPurse();
  80. }
  81. get shoes():OutfitItemShoes{
  82. let shoesId = this.wornItemIds.shoes;
  83. if(shoesId)
  84. return this.item(shoesId) as OutfitItemShoes;
  85. return new OutfitItemShoes();
  86. }
  87. //#endregion
  88. _lastWorn:{[key:string]:WornOutfitDefinition} = {
  89. last:{
  90. bra: null,
  91. coat: null,
  92. clothes: null,
  93. panties: null,
  94. purse: null,
  95. shoes: null
  96. }
  97. }
  98. _outfits:{[key:string]:WornOutfitDefinition} = {
  99. }
  100. outfitSports = '';
  101. outfitSchool = '';
  102. get outfits(){
  103. return this._outfits;
  104. }
  105. clothingDecreaseHealth(v){
  106. let clothesId = this._wornItems.clothes;
  107. if(clothesId in this._ownedItems){
  108. if(this._ownedItems[clothesId].h > 0){ //Health -1 is save
  109. this._ownedItems[clothesId].h = Math.max(0,this._ownedItems[clothesId].h - v);
  110. }
  111. }
  112. }
  113. /*get clothingIsNude(){
  114. return (!this._wornItems.clothes);
  115. }*/
  116. /*get clothingWorn(){
  117. return this._wornItems.clothes;
  118. }*/
  119. /*get clothingworntype(){
  120. if(!this._wornItems.clothes)
  121. return 'nude';
  122. let identificator = this.itemData(this._wornItems.clothes) as OutfitDefinitionClothes;
  123. return identificator.vendor+'_'+identificator.subtype;
  124. }*/
  125. /*
  126. get clothingwornnumber(){
  127. if(!this._wornItems.clothes)
  128. return 0;
  129. let identificator = this.itemData(this._wornItems.clothes) as OutfitDefinitionClothes;
  130. return identificator.index;
  131. }*/
  132. /*get coatworntype(){
  133. if(!this._wornItems.coat)
  134. return 'none';
  135. let identificator = this.itemData(this._wornItems.coat);
  136. return identificator.vendor;
  137. }*/
  138. /*get coatwornnumber(){
  139. if(!this._wornItems.coat)
  140. return 0;
  141. let identificator = this.itemData(this._wornItems.coat);
  142. return identificator.index;
  143. }*/
  144. /*get isWearingBra(){
  145. return (!(!this._wornItems.bra));
  146. }*/
  147. /*get isWearingPanties(){
  148. return (!(!this._wornItems.panties));
  149. }*/
  150. get isNaked(){
  151. return (!this.clothes.isValidItem && !this.bra.isValidItem && !this.panties.isValidItem);
  152. }
  153. /*get PCloQuality(){
  154. if(this.clothingIsNude){
  155. let bmi = State.variables.pc.bmi;
  156. if(bmi >= 19 && bmi < 30)
  157. return 3;
  158. return 1;
  159. }
  160. return this.wornItemData('clothes','quality',0)
  161. }
  162. get PCloThinness(){return this.wornItemData('clothes','thinness',0)}
  163. get PCloTopCut(){
  164. const pc = State.variables.pc;
  165. let wiD = this.wornItemData('clothes','CloTopCut',0);
  166. if(wiD > 1){
  167. if(pc.tits == 2 || pc.tits == 3)
  168. wiD += 1;
  169. else if(pc.tits >= 4)
  170. wiD += 2;
  171. if(wiD > 2 && pc.tits >= 6)
  172. wiD += 1;
  173. }
  174. return wiD;
  175. }
  176. get PCloBra(){return this.wornItemData('clothes','includesBra',0)}
  177. get PCloOnePiece(){return this.wornItemData('clothes','isOnepiece',0)}
  178. get PCloPants(){return this.wornItemData('clothes','pantsShortness',0)}
  179. get PCloSkirt(){return this.wornItemData('clothes','skirtShortness',0)}
  180. get skirtLength(){return this.PCloSkirt;}
  181. get PCloPanties(){return this.wornItemData('clothes','includesPanties',0)}
  182. get PCloDress(){return this.wornItemData('clothes','isDress',0)}
  183. get PCloStyle(){return this.wornItemData('clothes','style',0)}
  184. get PCloStyle2(){return this.wornItemData('clothes','style2',0)}
  185. get PCloStyle3(){return this.wornItemData('clothes','style3',0)}
  186. get PCloInhibit(){return this.wornItemData('clothes','inhibition',0)}
  187. get PCloBimbo(){return this.wornItemData('clothes','bimbo',0)}
  188. get PCloCoverTop(){return this.wornItemData('clothes','coverTop',4)}
  189. get PCloCoverBack(){return this.wornItemData('clothes','coverBack',4)}
  190. get PCloCoverFront(){return this.wornItemData('clothes','coverFront',4)}
  191. //get PCloCoverFront(){return this.wornItemData('clothes','coverFront',0)}
  192. */
  193. /*get PXCloThinness(){
  194. let PCloThinness = this.PCloThinness;
  195. switch(PCloThinness){
  196. case 1: return 150;
  197. case 2: return 200;
  198. case 3: return 250;
  199. case 4: return 300;
  200. case 5: return 350;
  201. case 6: return 400;
  202. }
  203. if(PCloThinness > 6)
  204. return 400;
  205. return 0;
  206. }*/
  207. /*get PXCloTopCut(){
  208. if(this.PCloBra == 1) return 400;
  209. if(this.PCloBra == 2) return 500;
  210. let PCloTopCut = this.PCloTopCut;
  211. switch(PCloTopCut){
  212. case 1: return 100;
  213. case 2: return 150;
  214. case 3: return 200;
  215. case 4: return 250;
  216. case 5: return 300;
  217. case 6: return 350;
  218. case 7: return 400;
  219. }
  220. if(PCloTopCut > 7)
  221. return 400;
  222. return 0;
  223. }*/
  224. /*get PXCloBottomShortness(){
  225. if(this.PCloPanties == 1) return 400;
  226. let PCloSkirt = this.PCloSkirt;
  227. if(PCloSkirt > 0){
  228. switch(PCloSkirt){
  229. case 1: return 100;
  230. case 2: return 150;
  231. case 3: return 200;
  232. case 4: return 250;
  233. case 5: return 300;
  234. case 6: return 350;
  235. case 7: return 400;
  236. }
  237. if(this.PCloThinness > 7)
  238. return 400;
  239. }
  240. let PCloPants = this.PCloPants;
  241. if(PCloPants > 0){
  242. switch(PCloPants){
  243. case 1: return 100;
  244. case 2: return 150;
  245. case 3: return 200;
  246. case 4: return 250;
  247. case 5: return 300;
  248. case 6: return 350;
  249. case 7: return 400;
  250. }
  251. if(PCloPants > 7)
  252. return 400;
  253. }
  254. return 0;
  255. }*/
  256. /*get PCloswimwear(){
  257. if(!this._wornItems.clothes)
  258. return 0;
  259. let identificator = this.itemData(this._wornItems.clothes) as OutfitDefinitionClothes;
  260. if(identificator.subtype == 'bikini' || identificator.subtype == 'swimsuit' || identificator.subtype == 'swim_one' || identificator.subtype == 'swim_two')
  261. return 1;
  262. return 0;
  263. }*/
  264. /*get isWearingSwimwear(){
  265. return !(!this.PCloswimwear);
  266. }*/
  267. /*get CoverTop(){return Math.max(this.PCloCoverTop + this.PBraCover - 4,0);}
  268. get CoverBack(){return Math.max(this.PCloCoverBack + this.PPanCoverBack - 4,0);}
  269. get CoverFront(){return Math.max(this.PCloCoverFront + this.PPanCoverFront - 4,0);}
  270. get isMaidOutfit(){
  271. return this.wornItemData('clothes','subtype','') === 'maid';
  272. }
  273. get isSchoolOutfit(){
  274. return this.wornItemData('clothes','subtype','') === 'school';
  275. }
  276. get isSportsOutfit(){
  277. return this.clothingworntype === 'danilovich_outfit';
  278. }*/
  279. // ----- Bra -----
  280. /*get braworntype(){
  281. if(!this._wornItems.bra)
  282. return 'none';
  283. let identificator = this.itemData(this._wornItems.bra);
  284. return identificator.vendor;
  285. }
  286. get brawornnumber(){
  287. if(!this._wornItems.bra)
  288. return 0;
  289. let identificator = this.itemData(this._wornItems.bra);
  290. return identificator.index;
  291. }
  292. get PBraMaterial(){return this.wornItemData('bra','material',0)}
  293. get PBraType(){return this.wornItemData('bra','subtype',0)}
  294. get PBraFun(){return this.wornItemData('bra','fun',0)}
  295. get PBraQuality(){return this.wornItemData('bra','quality',0)}
  296. get PBraThinness(){return this.wornItemData('bra','thinness',0)}
  297. get PBraCover(){return this.wornItemData('bra','cover',0)}
  298. get bra_appearanceBonus(){
  299. //Todo: Should be saved in const data
  300. switch(this.braworntype){
  301. case 'lusso': return 4;
  302. case 'fashionista': return 2;
  303. }
  304. return 0;
  305. }*/
  306. // ----- Panties -----
  307. /*
  308. get pantyworntype(){ //<<set $pantyworntype = $location_var[$here][1]>>
  309. if(!this._wornItems.panties)
  310. return 'none';
  311. let identificator = this.itemData(this._wornItems.panties);
  312. return identificator.vendor;
  313. }
  314. get pantywornnumber(){ //<<set $pantywornnumber = $location_var[$here][2]>>
  315. if(!this._wornItems.panties)
  316. return 0;
  317. let identificator = this.itemData(this._wornItems.panties);
  318. return identificator.index;
  319. }
  320. get PPanMaterial(){return this.wornItemData('panties','material',0)}
  321. get PPantyFun(){return this.wornItemData('panties','fun',0)}
  322. get PPanQuality(){return this.wornItemData('panties','quality',0)}
  323. get PPanThinness(){return this.wornItemData('panties','thinness',0)}
  324. get PPanCoverFront(){return this.wornItemData('panties','coverFront',0)}
  325. get PPanCoverBack(){return this.wornItemData('panties','coverBack',0)}
  326. get PPanType(){return this.wornItemData('panties','subtype',0)}
  327. get panties_appearanceBonus(){
  328. //Todo: Should be saved in const data
  329. switch(this.pantyworntype){
  330. case 'lusso': return 4;
  331. case 'fashionista': return 2;
  332. }
  333. return 0;
  334. }*/
  335. // ----- Shoes -----
  336. get isWearingShoes(){
  337. //return !(!this._wornItems.shoes);
  338. if(!this.shoes.isValidItem)
  339. return false;
  340. if(!State.variables.settings.shoes_keepOnPrivateIndoors && !State.variables.location.isPublic && !State.variables.location.isOutdoors)
  341. return false;
  342. return true;
  343. }
  344. itemImage(itemId:string):string{
  345. return OutfitItem.get(itemId).image;
  346. }
  347. /*get shoeworntype(){ //<<set $pantyworntype = $location_var[$here][1]>>
  348. if(!this._wornItems.shoes)
  349. return 'none';
  350. let identificator = this.itemData(this._wornItems.shoes);
  351. return identificator.vendor;
  352. }
  353. get shoewornnumber(){ //<<set $pantywornnumber = $location_var[$here][2]>>
  354. if(!this._wornItems.shoes)
  355. return 0;
  356. let identificator = this.itemData(this._wornItems.shoes);
  357. return identificator.index;
  358. }
  359. get PShoQuaility(){
  360. if(this.isWearingShoes)
  361. return this.wornItemData('shoes','quality',0)
  362. return 0;
  363. }
  364. get PShoCut(){
  365. if(this.isWearingShoes)
  366. return this.wornItemData('shoes','cut',0)
  367. return 0;
  368. }
  369. get PShoHeels(){
  370. if(this.isWearingShoes)
  371. return this.wornItemData('shoes','heels',0)
  372. return 0;
  373. }
  374. get PShoStyle(){
  375. if(this.isWearingShoes)
  376. return this.wornItemData('shoes','style',0)
  377. return 0;
  378. }*/
  379. /**
  380. * Return heel height of worn shoes in mm.
  381. * @readonly
  382. * @type {(0 | 60 | 100 | 150 | 200 | 240 | 280 | 300)}
  383. */
  384. /*get shoesHeight(){
  385. switch(this.PShoHeels){
  386. case 0: return 0;
  387. case 1: return 0;
  388. case 2: return 60;
  389. case 3: return 100;
  390. case 4: return 150;
  391. case 5: return 200;
  392. case 6: return 240;
  393. case 7: return 280;
  394. default: return 300;
  395. }
  396. }*/
  397. /**
  398. * Appearance Bonus of worn shoes.
  399. * @date 6/16/2023 - 7:05:32 AM
  400. *
  401. * @readonly
  402. * @type {(25 | 50 | 100 | 150 | 200 | 300 | 400 | 0)}
  403. */
  404. /*get PXShoHeels(){
  405. let PShoHeels = this.PShoHeels;
  406. if(PShoHeels > 0){
  407. switch(PShoHeels){
  408. case 1: return 25;
  409. case 2: return 50;
  410. case 3: return 100;
  411. case 4: return 150;
  412. case 5: return 200;
  413. case 6: return 300;
  414. case 7: return 400;
  415. }
  416. if(PShoHeels > 7)
  417. return 400;
  418. }
  419. return 0;
  420. }*/
  421. /*get shoes_appearanceBonus(){
  422. return this.PShoQuaility;
  423. }*/
  424. // ----- Coats -----
  425. /*get PCoatWarm(){return this.wornItemData('coat','warm',0)}
  426. get PCoatQuality(){return this.wornItemData('coat','quality',0)}
  427. get coat_appearanceBonus(){
  428. let location = State.variables.location;
  429. if(location.isOutdoors)
  430. return this.PCoatQuality - 2;
  431. return 0;
  432. }*/
  433. // ----- Purses -----
  434. /*get purseEquipped(){
  435. return (this._wornItems.purse) ? true : false;
  436. }
  437. */
  438. get outfitQuality(){
  439. if(this.isWearingShoes)
  440. return Math.floor((this.clothes.quality * 2 + this.shoes.quality) / 3);
  441. return this.clothes.quality;
  442. }
  443. get outfitIndecency(){
  444. return setup.func('outfitIndecency');
  445. }
  446. /**
  447. * Low-Level adds an item to the wardrobe.
  448. * @param {string} itemId
  449. * @param {{}} [metadata={}]
  450. */
  451. add(itemId:string,metadata: {}={}){
  452. let constData = this.itemConstantData(itemId);
  453. let type_defaults = setup.outfitsDefaults[constData.type] || {};
  454. let vendor_defaults = setup.outfitsDefaults[constData.vendor] || {};
  455. metadata = Object.assign({},type_defaults,vendor_defaults,metadata);
  456. let iterateObject = clone(metadata);
  457. for (const [key, value] of Object.entries(iterateObject)) {
  458. if(typeof value === "string" && value.startsWith('$'))
  459. metadata[key] = State.getVar(value);
  460. }
  461. this._ownedItems[itemId] = metadata;
  462. console.log("ITEM ADDED",itemId,metadata);
  463. }
  464. /**
  465. * Low-Level removes an item from the wardrobe.
  466. * @param {string} itemId
  467. */
  468. delete(itemId:string){
  469. delete this._ownedItems[itemId];
  470. }
  471. deleteOutfit(outfitId:string){
  472. return delete this.outfits[outfitId];
  473. }
  474. dispose(itemId:string){
  475. this.delete(itemId);
  476. }
  477. disposeWorn(type:string){
  478. let itemId = this._wornItems[type];
  479. if(itemId){
  480. this.strip(type);
  481. this.dispose(itemId);
  482. }
  483. }
  484. /**
  485. * All defined items.
  486. */
  487. get allItems(): {[key: string]: OutfitDefinition}{
  488. return setup.outfits;
  489. }
  490. allItemsFiltered(filter:Filter){
  491. return setup.filterDict(this.allItems,filter);
  492. }
  493. allItemIdsFiltered(filter:Filter,count=undefined,randomOrder=false){
  494. let itemIds = Object.keys(this.allItemsFiltered(filter));
  495. if(randomOrder)
  496. itemIds.shuffle();
  497. if(count)
  498. itemIds = itemIds.slice(0,count);
  499. return itemIds;
  500. }
  501. /**
  502. * Returns all items (owned or not) that match filters.
  503. * @param {*} filters
  504. * @returns
  505. */
  506. /*getItems(filters={}):{[key:string]:OutfitDefinition}{
  507. let items:{[key:string]:OutfitDefinition} = this.filterItems(this.allItems,filters);
  508. return items;
  509. }*/
  510. /*getItemIds(filters={},count=undefined,randomOrder=false){
  511. let itemIds = Object.keys(this.getItems(filters))
  512. if(randomOrder)
  513. itemIds.shuffle();
  514. if(count)
  515. itemIds = itemIds.slice(0,count);
  516. return itemIds;
  517. }*/
  518. /*filterItems(items:{[key:string]:OutfitDefinition},filters:Filter):{[key:string]:OutfitDefinition}{
  519. return setup.filterDict(items,filters);
  520. }*/
  521. // Gets a list of owned items
  522. /*getList(filters={}){
  523. let results = [];
  524. itemloop: for (const [item_id, item_data] of Object.entries(this._ownedItems)) {
  525. let itemData = this.get(item_id);
  526. let vendor = itemData.vendor;
  527. let type = itemData.type;
  528. let subtype = itemData.subtype;
  529. let index = itemData.index;
  530. filterloop: for (const [filter_key, filter_value] of Object.entries(filters)) {
  531. if(filter_value === null || filter_value === undefined)
  532. continue filterloop;
  533. if(filter_key == 'type'){
  534. if(type != filter_value)
  535. continue itemloop;
  536. continue filterloop;
  537. }else if(filter_key == 'vendor'){
  538. if(vendor != filter_value)
  539. continue itemloop;
  540. continue filterloop;
  541. }else if(filter_key == 'subtype'){
  542. if(subtype != filter_value)
  543. continue itemloop;
  544. continue filterloop;
  545. }else if(filter_key == 'index'){
  546. if(index != filter_value)
  547. continue itemloop;
  548. continue filterloop;
  549. }else if(filter_key == 'location'){
  550. if(Array.isArray(filter_value)){
  551. if(!filter_value.includes(itemData[filter_key]))
  552. continue itemloop;
  553. continue filterloop;
  554. }
  555. if(filter_value == -1)
  556. continue filterloop;
  557. if(filter_value == 0){
  558. if(!("location" in itemData) || itemData.location == 0)
  559. continue filterloop;
  560. continue itemloop;
  561. }
  562. if(!("location" in itemData))
  563. continue itemloop;
  564. if(itemData.location != filter_value)
  565. continue itemloop;
  566. continue filterloop;
  567. }else{
  568. if(Array.isArray(filter_value)){
  569. if(!filter_value.includes(itemData[filter_key] || 0))
  570. continue itemloop;
  571. continue filterloop;
  572. }else if(['string','number'].includes(typeof filter_value)){
  573. if(itemData[filter_key] != filter_value)
  574. continue itemloop;
  575. continue filterloop;
  576. }else{
  577. console.warn('Unreckognized filter format in $wardrobe.getlist():',filter_value);
  578. }
  579. }
  580. }
  581. let result = {
  582. id: item_id,
  583. type: type,
  584. vendor: vendor,
  585. subtype: subtype,
  586. index: index,
  587. image: this.itemImage(item_id),
  588. location: ("l" in item_data) ? item_data.l : 0,
  589. health: ("h" in item_data) ? item_data.h : -1,
  590. }
  591. results.push(result);
  592. }
  593. console.log("wardrobe.getList",filters,results);
  594. return results;
  595. }*/
  596. has(itemId){
  597. return (itemId in this._ownedItems);
  598. }
  599. inhibition(itemId:string){
  600. return OutfitItem.get(itemId).inhibition;
  601. }
  602. isWearing(itemId:string):boolean{
  603. for(const wornItemId of Object.values(this._wornItems)){
  604. if(itemId == wornItemId)
  605. return true;
  606. }
  607. return false;
  608. }
  609. isWeargingItemAtSlot(type){
  610. return(!(this._wornItems[type] == null));
  611. }
  612. /**
  613. * @deprecated
  614. * @param {string} itemId
  615. * @returns {OutfitDefinition}
  616. */
  617. itemConstantData(itemId:string):OutfitDefinition{
  618. if(!setup.outfits[itemId])
  619. return null;
  620. let raw = setup.outfits[itemId];
  621. return raw;
  622. }
  623. /**
  624. * Return the constant data of an item
  625. * @deprecated
  626. * @param {string} itemId
  627. * @returns
  628. */
  629. itemData(itemId:string):OutfitDefinition{
  630. let constantData = this.itemConstantData(itemId);
  631. return constantData;
  632. }
  633. /*itemImage(itemId){
  634. let identificator = this.itemData(itemId);
  635. let template = setup.outfitItemImagePath[identificator.type];
  636. if(!template)
  637. console.error("ERROR: template not found",identificator);
  638. if(typeof template === "string")
  639. return template.formatUnicorn({vendor:identificator.vendor,id:identificator.index});
  640. if(!template[identificator.vendor]?.[identificator.subtype])
  641. console.error("ERROR: item-image-template is undefined",identificator,template[identificator.vendor]);
  642. return template[identificator.vendor][identificator.subtype].formatUnicorn({vendor:identificator.vendor,id:identificator.index});
  643. }*/
  644. /*
  645. itemImageAtSlot(slotId){
  646. return this.itemImage(this._wornItems[slotId]);
  647. }*/
  648. itemPrice(itemId,baseprice,pricemod=0){
  649. let itemData = this.itemData(itemId);
  650. let type = itemData.type;
  651. if(type != 'shoes' && type != 'clothes')
  652. return baseprice;
  653. let quality = itemData.quality;
  654. let price_raw = (baseprice * ((5 * quality) + 100) / 100) * 1000 / (1250 - pricemod) * 3 / 2;
  655. let price_rounded = Math.round(price_raw / 50) * 50;
  656. return price_rounded;
  657. }
  658. lastWornSet(key){
  659. this._lastWorn[key] = clone(this._wornItems);
  660. }
  661. //#region Outfit
  662. outfitAdd(key, outfitData=null){
  663. if(!this._outfits)
  664. this._outfits = {};
  665. if(outfitData)
  666. this._outfits[key] = clone(outfitData);
  667. else
  668. this._outfits[key] = clone(this._wornItems);
  669. }
  670. outfitIsWearable(key){
  671. if(!(key in this._outfits)){
  672. return false;
  673. }
  674. let slots = ['bra','clothes','panties','shoes'];
  675. for(let slot of slots){
  676. let wearable = this.wearable(this._outfits[key][slot]);
  677. if(!wearable.total){
  678. return false;
  679. }
  680. }
  681. return true;
  682. }
  683. //#endregion
  684. get isWearingSwimwear():boolean{
  685. return (this.clothes.type == 'swimwear');
  686. }
  687. get ownsSwimmingItem():boolean{
  688. //return (this.getList({subtype:'bikini'}).length + this.getList({subtype:'swimsuit'}).length > 0) ? true : false;
  689. return (Object.keys(this.itemsFiltered({type:'swimwear'})).length > 0);
  690. }
  691. /**
  692. * Sends an item to the specified location.
  693. * Side effect: adds the item to the inventory if it isn't already in it.
  694. * @param {*} itemIdentification
  695. * @param {number} location
  696. */
  697. sendToLocation(itemId,location){
  698. let set = {l:location};
  699. this.setMetadata(itemId,set);
  700. }
  701. setMetadata(itemId,metadata){
  702. let temp = Object.assign({}, this._ownedItems[itemId], metadata);
  703. console.log("Item METADATA SET:",itemId,metadata,this._ownedItems[itemId],temp);
  704. this._ownedItems[itemId] = temp;
  705. }
  706. strip(type:string){
  707. let item:OutfitItem = this[type];
  708. if(!item.isValidItem)
  709. return;
  710. for(const slot of item.slots)
  711. this._wornItems[slot] = null;
  712. }
  713. stripAll(){
  714. this.strip('bra');
  715. this.strip('clothes');
  716. this.strip('panties');
  717. this.strip('shoes');
  718. }
  719. wearable(itemId:string){
  720. let result = {
  721. owned: true,
  722. health: true,
  723. inhibition: true,
  724. hips: true,
  725. various:[],
  726. total: true
  727. }
  728. var itemData:OutfitOwned;
  729. if(!this.has(itemId)){
  730. result.owned = false;
  731. result.health = false;
  732. result.hips = false;
  733. result.total = false;
  734. return result;
  735. }else{
  736. itemData = this.item(itemId);
  737. if(itemData.h == 0)
  738. result.health = false;
  739. if(itemData.bmi > 0){
  740. result.hips = (this.wearableBMI(itemId) != 0 && this.wearableBMI(itemId) != 1);
  741. }
  742. }
  743. /*let constData = setup.itemLoadConstData(
  744. identificator.type,
  745. identificator.vendor,
  746. identificator.index,
  747. identificator.subtype);*/
  748. if(!this.wearbleInhibition(itemId))
  749. result.inhibition = false;
  750. result.total = (result.owned && result.health && result.inhibition && result.hips);
  751. for(const wardrobeWearableFunction of Object.values(setup.wardrobeWearableFunctions)){
  752. let functionValue = wardrobeWearableFunction(itemData);
  753. if(functionValue){
  754. result.total = false;
  755. result.various.push(functionValue);
  756. }
  757. }
  758. return result;
  759. }
  760. /**
  761. *
  762. * @param {string} itemId
  763. * @returns {number} -2 If concept of BMI is not aplicable, -1 if suits any BMI, 0 if too lose, 1 if too tight, 2 if close to too lose, 3 if close to too tight, 4 of okay
  764. */
  765. wearableBMI(itemId){
  766. let itemData = this.item(itemId);
  767. if(itemData.bmi === undefined)
  768. return -2;
  769. if(!itemData.bmi)
  770. return -1;
  771. if(itemData.bmi < 0)
  772. return -2;
  773. let pcbmi = State.variables.pc.bmi;
  774. if(itemData.bmi - pcbmi > setup.wardrobeSettings.bmiSizeMaxDifference)
  775. return 1;
  776. if(pcbmi - itemData.bmi > setup.wardrobeSettings.bmiSizeMaxDifference)
  777. return 0;
  778. if(itemData.bmi - pcbmi > setup.wardrobeSettings.bmiSizeMaxDifference * 0.75)
  779. return 3;
  780. if(pcbmi - itemData.bmi > setup.wardrobeSettings.bmiSizeMaxDifference * 0.75)
  781. return 2;
  782. return 4;
  783. }
  784. //Return 0 if inhibition blocks, 1 if exciting and 2 in every other case
  785. wearbleInhibition(itemId){
  786. const inhibition = this.inhibition(itemId);
  787. let playerInhib = State.variables.pc.pcs_inhib;
  788. if(inhibition <= playerInhib)
  789. return 2;
  790. if(inhibition - 10 <= playerInhib)
  791. return 1;
  792. return 0;
  793. }
  794. /**
  795. * Wear the specified item. Send it to the default wardrobe section. Adds the item to the inventory, if it isn't in it.
  796. * @param {*} itemIdentification
  797. * @param {boolean} [replace=true] replace what's currently worn? If false only adds the item to the inventory.
  798. */
  799. wear(itemId,replace=true):boolean{
  800. if(!this.has(itemId))
  801. this.add(itemId);
  802. //let itemType = this.itemData(itemId).type;
  803. if(!replace){
  804. let item = OutfitItem.get(itemId);
  805. for(const slot of item.slots){
  806. let itemAtSlot:OutfitItem = this[slot];
  807. if(itemAtSlot.isValidItem)
  808. return false;
  809. }
  810. }
  811. this.sendToLocation(itemId,0);
  812. console.log("WEARING NOW",itemId);
  813. this._wear(itemId);
  814. return true;
  815. }
  816. wearTemporary(itemId){
  817. let itemType = this.itemData(itemId).type;
  818. this.strip(itemType);
  819. console.log("WEARING NOW",itemId,itemType);
  820. this._wear(itemId);
  821. }
  822. /*wear_clothes_legacy(type,vendorAndSubtype,index){
  823. let parts = vendorAndSubtype.split('_',2);
  824. this.wear([type,parts[0],index,parts[1]]);
  825. }*/
  826. //Caution: Unlike wear, this will not change the location of the worn item.
  827. _wear(itemOrId:string|OutfitItem){
  828. let item:OutfitItem;
  829. if(typeof itemOrId == "string")
  830. item = OutfitItem.get(itemOrId);
  831. else
  832. item = itemOrId;
  833. for(const slot of item.slots){
  834. this.strip(slot);
  835. this._wornItems[slot] = item.id;
  836. this._lastWorn['last'][slot] = item.id;
  837. }
  838. /*let itemType = this.itemData(itemId).type;
  839. this._wornItems[itemType] = itemId;
  840. this._lastWorn['last'][itemType] = itemId;*/
  841. /*if(this.PCloBra == 1)
  842. this.strip('bra');
  843. */
  844. }
  845. /*wear_last(type){
  846. this._wear(this._lastWorn['last'][type]);
  847. }*/
  848. hasLastWorn(key){
  849. return !(!this._lastWorn[key]);
  850. }
  851. wearLastWorn(key,consume=false){
  852. if(!this._lastWorn[key]){
  853. console.error("The last item set with key "+key+" has not been set!");
  854. }
  855. this._wornItems = clone(this._lastWorn[key]);
  856. if(consume)
  857. delete this._lastWorn[key];
  858. }
  859. wearOutfit(key){
  860. this._wornItems = clone(this._outfits[key]);
  861. }
  862. wornItemData(type,index,def:any=0){
  863. if(!this._wornItems[type])
  864. return def;
  865. let id = this._wornItems[type];
  866. let data = this.itemData(id);
  867. let returnValue = data[index]
  868. if(returnValue == undefined || returnValue == null)
  869. return def;
  870. return returnValue;
  871. }
  872. _shopitems = {} //{shopid:{expireday:day,pricemod:pricemod,list:[]}}
  873. shopItemIds(shopid:string,filters,count=30,expireday=undefined):string[]
  874. {
  875. if(shopid in this._shopitems){
  876. if(this._shopitems[shopid].expireday >= State.variables.time.daystart){
  877. let cachedList = this._shopitems[shopid].list;
  878. console.warn('Returning cached list',cachedList);
  879. return cachedList;
  880. }
  881. }
  882. let pricemod = rand(0,500);
  883. expireday ??= State.variables.time.daystart;
  884. let entrys:string[] = this.allItemIdsFiltered(filters,count,true);
  885. entrys = entrys.filter(itemId=>!this.has(itemId));
  886. this.shopItemListSet(shopid,expireday,pricemod,entrys);
  887. return entrys;
  888. }
  889. shopItemList(shopid,filters,baseprice,count=30,expireday=null):{[itemId:string]:OutfitItem}{
  890. let result:{[itemId:string]:OutfitItem} = {};
  891. for(const itemId of this.shopItemIds(shopid,filters,count,expireday)){
  892. let item = OutfitItem.get(itemId);
  893. Object.assign(item,{id: itemId,price: this.itemPrice(itemId,baseprice)});
  894. result[itemId] = item;
  895. }
  896. return result;
  897. }
  898. shopItemListSet(shopid,expireday,pricemod,items){
  899. this._shopitems[shopid] = {
  900. expireday: expireday,
  901. pricemod:pricemod,
  902. list: items
  903. }
  904. }
  905. filters = {
  906. shop_cost:true,
  907. shop_owned:false,
  908. shop_inhib_toohigh:false,
  909. shop_inhib_high:true,
  910. shop_inhib_low:true
  911. };
  912. /*execute_every_1_hour(){
  913. }
  914. execute_every_1_day(){
  915. }*/
  916. _init(wardrobe){
  917. Object.keys(wardrobe).forEach(function (pn) {
  918. this[pn] = clone(wardrobe[pn]);
  919. }, this);
  920. return this;
  921. }
  922. clone = function () {
  923. return (new Wardrobe())._init(this);
  924. };
  925. toJSON = function () {
  926. var ownData = {};
  927. Object.keys(this).forEach(function (pn) {
  928. if(typeof this[pn] !== "function")
  929. ownData[pn] = clone(this[pn]);
  930. }, this);
  931. return JSON.reviveWrapper('(new Wardrobe())._init($ReviveData$)', ownData);
  932. };
  933. }
  934. window.Wardrobe = Wardrobe;
  935. setup.outfits = {};